Skip to main content

deribit_http/endpoints/
public.rs

1//! REST API endpoints implementation
2//!
3//! This module implements all Deribit REST API endpoints including
4//! market data, trading, account management, and system endpoints.
5
6use crate::DeribitHttpClient;
7use crate::constants::endpoints::*;
8use crate::error::HttpError;
9use crate::model::LastTradesResponse;
10use crate::model::book::{BookSummary, OrderBook};
11use crate::model::currency::CurrencyStruct;
12use crate::model::funding::{FundingChartData, FundingRateData};
13use crate::model::index::{IndexChartDataPoint, IndexData, IndexPriceData};
14use crate::model::instrument::{Instrument, OptionType};
15use crate::model::order::OrderSide;
16use crate::model::other::{OptionInstrument, OptionInstrumentPair};
17use crate::model::response::api_response::ApiResponse;
18use crate::model::response::other::{
19    AprHistoryResponse, ContractSizeResponse, DeliveryPricesResponse, ExpirationsResponse,
20    IndexNameInfo, MarkPriceHistoryPoint, SettlementsResponse, StatusResponse, TestResponse,
21    TradeVolume, VolatilityIndexData,
22};
23use crate::model::ticker::TickerData;
24use crate::model::trade::{Liquidity, Trade};
25use crate::model::tradingview::TradingViewChartData;
26use std::collections::HashMap;
27
28/// Market data endpoints
29impl DeribitHttpClient {
30    /// Get all supported currencies
31    ///
32    /// Retrieves all cryptocurrencies supported by the API.
33    /// This is a public endpoint that doesn't require authentication.
34    ///
35    /// # Examples
36    ///
37    /// ```rust
38    /// # use deribit_http::DeribitHttpClient;
39    /// # #[tokio::main]
40    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
41    /// let client = DeribitHttpClient::new(); // testnet
42    /// let currencies = client.get_currencies().await?;
43    /// for currency in currencies {
44    ///     println!("Currency: {} ({})", currency.currency, currency.currency_long);
45    /// }
46    /// # Ok(())
47    /// # }
48    /// ```
49    pub async fn get_currencies(&self) -> Result<Vec<CurrencyStruct>, HttpError> {
50        self.public_get(GET_CURRENCIES, "").await
51    }
52
53    /// Get current index price for a currency
54    ///
55    /// Retrieves the current index price for the instruments, for the selected currency.
56    /// This is a public endpoint that doesn't require authentication.
57    ///
58    /// # Arguments
59    ///
60    /// * `currency` - The currency symbol (BTC, ETH, USDC, USDT, EURR)
61    ///
62    pub async fn get_index(&self, currency: &str) -> Result<IndexData, HttpError> {
63        let query = format!("?currency={}", currency);
64        self.public_get(GET_INDEX, &query).await
65    }
66
67    /// Get index price by name
68    ///
69    /// Retrieves the current index price value for given index name.
70    /// This is a public endpoint that doesn't require authentication.
71    ///
72    /// # Arguments
73    ///
74    /// * `index_name` - The index identifier (e.g., "btc_usd", "eth_usd")
75    ///
76    /// # Examples
77    ///
78    /// ```rust
79    /// # use deribit_http::DeribitHttpClient;
80    /// # #[tokio::main]
81    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
82    /// let client = DeribitHttpClient::new(); // testnet
83    /// let index_price = client.get_index_price("btc_usd").await?;
84    /// println!("Index price: {}", index_price.index_price);
85    /// # Ok(())
86    /// # }
87    /// ```
88    pub async fn get_index_price(&self, index_name: &str) -> Result<IndexPriceData, HttpError> {
89        let query = format!("?index_name={}", index_name);
90        self.public_get(GET_INDEX_PRICE, &query).await
91    }
92
93    /// Get all supported index price names
94    ///
95    /// Retrieves the identifiers of all supported Price Indexes.
96    /// This is a public endpoint that doesn't require authentication.
97    ///
98    /// # Examples
99    ///
100    /// ```rust
101    /// # use deribit_http::DeribitHttpClient;
102    /// # #[tokio::main]
103    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
104    /// let client = DeribitHttpClient::new(); // testnet
105    /// let index_names = client.get_index_price_names().await?;
106    /// for name in index_names {
107    ///     println!("Index: {}", name);
108    /// }
109    /// # Ok(())
110    /// # }
111    /// ```
112    pub async fn get_index_price_names(&self) -> Result<Vec<String>, HttpError> {
113        self.public_get(GET_INDEX_PRICE_NAMES, "").await
114    }
115
116    /// Get index chart data
117    ///
118    /// Returns historical price index chart data for the specified index name and time range.
119    /// The data is formatted for use in charting applications and shows price index values over time.
120    /// This is a public endpoint that doesn't require authentication.
121    ///
122    /// # Arguments
123    ///
124    /// * `index_name` - Index identifier (e.g., "btc_usd", "eth_usd", "sol_usdc")
125    /// * `range` - Time range for the data: "1h", "1d", "2d", "1m", "1y", or "all"
126    ///
127    /// # Examples
128    ///
129    /// ```rust
130    /// # use deribit_http::DeribitHttpClient;
131    /// # #[tokio::main]
132    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
133    /// let client = DeribitHttpClient::new(); // testnet
134    /// let chart_data = client.get_index_chart_data("btc_usd", "1d").await?;
135    /// for point in chart_data {
136    ///     println!("Time: {}, Price: {}", point.timestamp, point.price);
137    /// }
138    /// # Ok(())
139    /// # }
140    /// ```
141    pub async fn get_index_chart_data(
142        &self,
143        index_name: &str,
144        range: &str,
145    ) -> Result<Vec<IndexChartDataPoint>, HttpError> {
146        let query = format!(
147            "?index_name={}&range={}",
148            urlencoding::encode(index_name),
149            urlencoding::encode(range)
150        );
151        self.public_get(GET_INDEX_CHART_DATA, &query).await
152    }
153
154    /// Get book summary by currency
155    ///
156    /// Retrieves the summary information such as open interest, 24h volume, etc.
157    /// for all instruments for the currency (optionally filtered by kind).
158    /// This is a public endpoint that doesn't require authentication.
159    ///
160    /// # Arguments
161    ///
162    /// * `currency` - The currency symbol (BTC, ETH, USDC, USDT, EURR)
163    /// * `kind` - Optional instrument kind filter (future, option, spot, future_combo, option_combo)
164    ///
165    /// # Examples
166    ///
167    /// ```rust
168    /// # use deribit_http::DeribitHttpClient;
169    /// # #[tokio::main]
170    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
171    /// let client = DeribitHttpClient::new(); // testnet
172    /// let summaries = client.get_book_summary_by_currency("BTC", Some("future")).await?;
173    /// for summary in summaries {
174    ///     println!("Instrument: {} - Volume: {}", summary.instrument_name, summary.volume);
175    /// }
176    /// # Ok(())
177    /// # }
178    /// ```
179    pub async fn get_book_summary_by_currency(
180        &self,
181        currency: &str,
182        kind: Option<&str>,
183    ) -> Result<Vec<BookSummary>, HttpError> {
184        let mut query = format!("?currency={}", currency);
185        if let Some(kind) = kind {
186            query.push_str(&format!("&kind={}", kind));
187        }
188        self.public_get(GET_BOOK_SUMMARY_BY_CURRENCY, &query).await
189    }
190
191    /// Get single instrument information
192    ///
193    /// Retrieves detailed information about a specific instrument.
194    /// This is a public endpoint that doesn't require authentication.
195    ///
196    /// # Arguments
197    ///
198    /// * `instrument_name` - The instrument identifier (e.g., "BTC-PERPETUAL")
199    ///
200    /// # Examples
201    ///
202    /// ```rust
203    /// # use deribit_http::DeribitHttpClient;
204    /// # #[tokio::main]
205    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
206    /// let client = DeribitHttpClient::new(); // testnet
207    /// let instrument = client.get_instrument("BTC-PERPETUAL").await?;
208    /// println!("Contract size: {:?}", instrument.contract_size);
209    /// # Ok(())
210    /// # }
211    /// ```
212    pub async fn get_instrument(&self, instrument_name: &str) -> Result<Instrument, HttpError> {
213        let query = format!("?instrument_name={}", instrument_name);
214        self.public_get(GET_INSTRUMENT, &query).await
215    }
216
217    /// Get book summary by instrument
218    ///
219    /// Retrieves the summary information such as open interest, 24h volume, etc.
220    /// for a specific instrument.
221    /// This is a public endpoint that doesn't require authentication.
222    ///
223    /// # Arguments
224    ///
225    /// * `instrument_name` - The instrument identifier (e.g., "BTC-PERPETUAL")
226    ///
227    pub async fn get_book_summary_by_instrument(
228        &self,
229        instrument_name: &str,
230    ) -> Result<BookSummary, HttpError> {
231        let query = format!("?instrument_name={}", instrument_name);
232        // The API returns an array with one element, so we parse as Vec and extract first
233        let book_summaries: Vec<BookSummary> = self
234            .public_get(GET_BOOK_SUMMARY_BY_INSTRUMENT, &query)
235            .await?;
236        book_summaries.into_iter().next().ok_or_else(|| {
237            HttpError::InvalidResponse("Empty book summary array in response".to_string())
238        })
239    }
240
241    /// Get contract size for an instrument
242    ///
243    /// Retrieves contract size for specified instrument.
244    /// This is a public endpoint that doesn't require authentication.
245    ///
246    /// # Arguments
247    ///
248    /// * `instrument_name` - The instrument identifier (e.g., "BTC-PERPETUAL")
249    ///
250    /// # Examples
251    ///
252    /// ```rust
253    /// # use deribit_http::DeribitHttpClient;
254    /// # #[tokio::main]
255    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
256    /// let client = DeribitHttpClient::new(); // testnet
257    /// let contract_size = client.get_contract_size("BTC-PERPETUAL").await?;
258    /// println!("Contract size: {}", contract_size);
259    /// # Ok(())
260    /// # }
261    /// ```
262    pub async fn get_contract_size(&self, instrument_name: &str) -> Result<f64, HttpError> {
263        let query = format!("?instrument_name={}", instrument_name);
264        let response: ContractSizeResponse = self.public_get(GET_CONTRACT_SIZE, &query).await?;
265        Ok(response.contract_size)
266    }
267
268    /// Get server time
269    ///
270    /// Returns the current server time in milliseconds since Unix epoch.
271    /// This is a public endpoint that doesn't require authentication.
272    ///
273    /// # Examples
274    ///
275    /// ```rust
276    /// # use deribit_http::DeribitHttpClient;
277    /// # #[tokio::main]
278    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
279    /// let client = DeribitHttpClient::new(); // testnet
280    /// let server_time = client.get_server_time().await?;
281    /// println!("Server time: {}", server_time);
282    /// # Ok(())
283    /// # }
284    /// ```
285    pub async fn get_server_time(&self) -> Result<u64, HttpError> {
286        self.public_get(GET_SERVER_TIME, "").await
287    }
288
289    /// Test connectivity to the API
290    ///
291    /// Returns the API version to test connectivity.
292    /// This is a public endpoint that doesn't require authentication.
293    pub async fn test_connection(&self) -> Result<String, HttpError> {
294        let response: TestResponse = self.public_get(TEST_CONNECTION, "").await?;
295        Ok(response.version)
296    }
297
298    /// Get platform status and locked currency indices
299    ///
300    /// Returns information about the platform status and any locked currency indices.
301    /// This is a public endpoint that doesn't require authentication.
302    ///
303    pub async fn get_status(&self) -> Result<StatusResponse, HttpError> {
304        let url = format!("{}{}", self.base_url(), GET_STATUS);
305
306        let response = self
307            .http_client()
308            .get(&url)
309            .send()
310            .await
311            .map_err(|e| HttpError::NetworkError(e.to_string()))?;
312
313        if !response.status().is_success() {
314            let error_text = response
315                .text()
316                .await
317                .unwrap_or_else(|_| "Unknown error".to_string());
318            return Err(HttpError::RequestFailed(format!(
319                "Get status failed: {}",
320                error_text
321            )));
322        }
323
324        // Try direct deserialization first (non-JSON-RPC response)
325        match response.json::<StatusResponse>().await {
326            Ok(status) => Ok(status),
327            Err(_) => {
328                // Fallback to JSON-RPC wrapper format
329                let response = self
330                    .http_client()
331                    .get(&url)
332                    .send()
333                    .await
334                    .map_err(|e| HttpError::NetworkError(e.to_string()))?;
335
336                let api_response: ApiResponse<StatusResponse> = response
337                    .json()
338                    .await
339                    .map_err(|e| HttpError::InvalidResponse(e.to_string()))?;
340
341                if let Some(error) = api_response.error {
342                    return Err(HttpError::RequestFailed(format!(
343                        "API error: {} - {}",
344                        error.code, error.message
345                    )));
346                }
347
348                api_response.result.ok_or_else(|| {
349                    HttpError::InvalidResponse("No status data in response".to_string())
350                })
351            }
352        }
353    }
354
355    /// Get APR history for yield tokens
356    ///
357    /// Retrieves historical APR data for specified currency. Only applicable to yield-generating tokens (USDE, STETH).
358    /// This is a public endpoint that doesn't require authentication.
359    ///
360    /// # Arguments
361    ///
362    /// * `currency` - Currency for which to retrieve APR history (usde or steth)
363    /// * `limit` - Optional number of days to retrieve (default 365, maximum 365)
364    /// * `before` - Optional parameter to receive APR history before given epoch day
365    ///
366    pub async fn get_apr_history(
367        &self,
368        currency: &str,
369        limit: Option<u32>,
370        before: Option<i32>,
371    ) -> Result<AprHistoryResponse, HttpError> {
372        let mut query = format!("?currency={}", currency);
373        if let Some(limit) = limit {
374            query.push_str(&format!("&limit={}", limit));
375        }
376        if let Some(before) = before {
377            query.push_str(&format!("&before={}", before));
378        }
379        self.public_get(GET_APR_HISTORY, &query).await
380    }
381
382    /// Get ticker information for an instrument
383    ///
384    /// Returns ticker data including last price, bid/ask, volume, etc.
385    ///
386    /// # Arguments
387    ///
388    /// * `instrument_name` - The instrument identifier (e.g., "BTC-PERPETUAL")
389    ///
390    /// # Examples
391    ///
392    /// ```rust
393    /// # use deribit_http::DeribitHttpClient;
394    /// # #[tokio::main]
395    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
396    /// let client = DeribitHttpClient::new();
397    /// let ticker = client.get_ticker("BTC-PERPETUAL").await?;
398    /// println!("Last price: {:?}", ticker.last_price);
399    /// # Ok(())
400    /// # }
401    /// ```
402    pub async fn get_ticker(&self, instrument_name: &str) -> Result<TickerData, HttpError> {
403        let query = format!("?instrument_name={}", instrument_name);
404        self.public_get(GET_TICKER, &query).await
405    }
406
407    /// Get order book for an instrument
408    ///
409    /// Returns the current order book with bids and asks.
410    ///
411    /// # Arguments
412    ///
413    /// * `instrument_name` - The instrument identifier
414    /// * `depth` - Optional depth of the order book (default: 5)
415    pub async fn get_order_book(
416        &self,
417        instrument_name: &str,
418        depth: Option<u32>,
419    ) -> Result<OrderBook, HttpError> {
420        let mut query = format!("?instrument_name={}", instrument_name);
421        if let Some(d) = depth {
422            query.push_str(&format!("&depth={}", d));
423        }
424        self.public_get(GET_ORDER_BOOK, &query).await
425    }
426
427    /// Retrieves a list of option instruments for a given currency and expiry date.
428    ///
429    /// This asynchronous function fetches option instruments for the specified `currency`
430    /// and `expiry` date and returns a filtered list of options along with their associated
431    /// ticker information.
432    ///
433    /// # Arguments
434    ///
435    /// * `currency` - A string slice that represents the name of the currency (e.g., "BTC", "ETH").
436    /// * `expiry` - A string slice representing the expiry date for the options (e.g., "20231027").
437    ///
438    /// # Returns
439    ///
440    /// Returns a `Result` containing a vector of `OptionInstrument` on success,
441    /// or an `HttpError` on failure.
442    ///
443    /// - On success, it returns a `Vec<OptionInstrument>`, where each option contains
444    ///   the instrument details and ticker information.
445    /// - On failure, it returns an `HttpError`, such as in cases where the instrument
446    ///   data could not be retrieved or tickers are inaccessible.
447    ///
448    /// # Errors
449    ///
450    /// This function may return an `HttpError` in the following scenarios:
451    ///
452    /// * If fetching the instrument data fails.
453    /// * If retrieving ticker information for an instrument fails.
454    ///
455    /// # Implementation Details
456    ///
457    /// 1. Fetches instruments for the specified `currency` filtered by type `option`.
458    /// 2. Filters the instruments to ensure they match the `currency`-`expiry` base name.
459    /// 3. Constructs an `OptionInstrument` for each filtered instrument, including
460    ///    the instrument details and ticker information.
461    ///
462    pub async fn get_options(
463        &self,
464        currency: &str,
465        expiry: &str,
466    ) -> Result<Vec<OptionInstrument>, HttpError> {
467        let mut instruments = self
468            .get_instruments(currency, Some("option"), Some(false))
469            .await
470            .map_err(|e| HttpError::RequestFailed(e.to_string()))?;
471
472        let base_name = format!("{}-{}", currency, expiry).to_uppercase();
473        // filter instruments by base name in instrument_name
474        instruments.retain(|i| i.instrument_name.starts_with(&base_name));
475
476        let mut options: Vec<OptionInstrument> = Vec::with_capacity(instruments.len());
477        for instrument in instruments {
478            let option = OptionInstrument {
479                instrument: instrument.clone(),
480                ticker: self.get_ticker(instrument.instrument_name.as_str()).await?,
481            };
482            options.push(option)
483        }
484        Ok(options)
485    }
486
487    /// Fetches option instruments for a given currency and expiry date, grouped by strike price.
488    ///
489    /// This method retrieves all option instruments for the specified currency and expiry,
490    /// then groups them into pairs (call and put) by strike price. Each strike price
491    /// maps to an `OptionInstrumentPair` containing the call and put options if available.
492    ///
493    /// # Arguments
494    ///
495    /// * `currency` - The currency symbol (e.g., "BTC", "ETH")
496    /// * `expiry` - The expiry date in format "DDMMMYY" (e.g., "10SEP25")
497    ///
498    /// # Returns
499    ///
500    /// Returns a `HashMap` where:
501    /// - Key: Strike price as `u64`
502    /// - Value: `OptionInstrumentPair` containing call and put options for that strike
503    ///
504    /// # Errors
505    ///
506    /// Returns `HttpError` if:
507    /// - The API request fails
508    /// - An option instrument has no option type
509    /// - Network or authentication errors occur
510    ///
511    /// # Example
512    ///
513    /// ```no_run
514    /// # use deribit_http::DeribitHttpClient;
515    /// # #[tokio::main]
516    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
517    /// let client = DeribitHttpClient::new();
518    /// let pairs = client.get_options_pair("BTC", "10SEP25").await?;
519    ///
520    /// for (strike, pair) in pairs {
521    ///     println!("Strike {}: Call={:?}, Put={:?}",
522    ///              strike, pair.call.is_some(), pair.put.is_some());
523    /// }
524    /// # Ok(())
525    /// # }
526    /// ```
527    pub async fn get_options_pair(
528        &self,
529        currency: &str,
530        expiry: &str,
531    ) -> Result<HashMap<u64, OptionInstrumentPair>, HttpError> {
532        let option_instruments = self.get_options(currency, expiry).await?;
533
534        let mut strikes_map: HashMap<u64, OptionInstrumentPair> =
535            HashMap::with_capacity(option_instruments.len() / 2);
536        for instrument in option_instruments {
537            let strike_price = instrument.instrument.strike.unwrap() as u64;
538            strikes_map
539                .entry(strike_price)
540                .or_insert(OptionInstrumentPair {
541                    call: None,
542                    put: None,
543                });
544            match instrument.instrument.option_type.clone() {
545                Some(option_type) => match option_type {
546                    OptionType::Call => {
547                        strikes_map.get_mut(&strike_price).unwrap().call = Some(instrument.clone());
548                    }
549                    OptionType::Put => {
550                        strikes_map.get_mut(&strike_price).unwrap().put = Some(instrument.clone());
551                    }
552                },
553                None => {
554                    return Err(HttpError::RequestFailed(
555                        "Option instrument has no option type".to_string(),
556                    ));
557                }
558            }
559        }
560
561        Ok(strikes_map)
562    }
563
564    /// Get available instruments
565    ///
566    /// Returns a list of available trading instruments.
567    ///
568    /// # Arguments
569    ///
570    /// * `currency` - The currency (e.g., "BTC", "ETH")
571    /// * `kind` - Optional instrument kind ("future", "option", "spot")
572    /// * `expired` - Whether to include expired instruments
573    pub async fn get_instruments(
574        &self,
575        currency: &str,
576        kind: Option<&str>,
577        expired: Option<bool>,
578    ) -> Result<Vec<Instrument>, HttpError> {
579        let mut query = format!("?currency={}", currency);
580        if let Some(k) = kind {
581            query.push_str(&format!("&kind={}", k));
582        }
583        if let Some(exp) = expired {
584            query.push_str(&format!("&expired={}", exp));
585        }
586        self.public_get(GET_INSTRUMENTS, &query).await
587    }
588
589    /// Get recent trades for an instrument
590    ///
591    /// Returns recent trade history for the specified instrument.
592    ///
593    /// # Arguments
594    ///
595    /// * `instrument_name` - The instrument identifier
596    /// * `count` - Optional number of trades to return (default: 10, max: 1000)
597    /// * `include_old` - Whether to include old trades
598    pub async fn get_last_trades(
599        &self,
600        instrument_name: &str,
601        count: Option<u32>,
602        include_old: Option<bool>,
603    ) -> Result<Vec<Trade>, HttpError> {
604        let mut query = format!("?instrument_name={}", urlencoding::encode(instrument_name));
605        if let Some(c) = count {
606            query.push_str(&format!("&count={}", c));
607        }
608        if let Some(old) = include_old {
609            query.push_str(&format!("&include_old={}", old));
610        }
611
612        let trades_response: LastTradesResponse = self
613            .public_get(GET_LAST_TRADES_BY_INSTRUMENT, &query)
614            .await?;
615
616        // Convert LastTrade to Trade
617        let trades: Vec<Trade> = trades_response
618            .trades
619            .into_iter()
620            .map(|last_trade| {
621                Trade {
622                    trade_id: last_trade.trade_id,
623                    instrument_name: last_trade.instrument_name,
624                    order_id: String::new(), // Not available in LastTrade
625                    direction: match last_trade.direction.as_str() {
626                        "buy" => OrderSide::Buy,
627                        "sell" => OrderSide::Sell,
628                        _ => OrderSide::Buy, // Default fallback
629                    },
630                    amount: last_trade.amount,
631                    price: last_trade.price,
632                    timestamp: last_trade.timestamp as i64,
633                    fee: 0.0,                    // Not available in LastTrade
634                    fee_currency: String::new(), // Not available in LastTrade
635                    liquidity: Liquidity::Taker, // Default
636                    mark_price: 0.0,             // Not available in LastTrade
637                    index_price: last_trade.index_price,
638                    instrument_kind: None, // Not available in LastTrade
639                    trade_seq: Some(last_trade.trade_seq),
640                    user_role: None,
641                    block_trade: None,
642                    underlying_price: None,
643                    iv: last_trade.iv,
644                    label: None,
645                    profit_loss: None,
646                    tick_direction: Some(last_trade.tick_direction),
647                    self_trade: None,
648                }
649            })
650            .collect();
651
652        Ok(trades)
653    }
654
655    /// Get historical volatility
656    ///
657    /// Provides information about historical volatility for given cryptocurrency.
658    ///
659    /// # Arguments
660    ///
661    /// * `currency` - Currency symbol (BTC, ETH, etc.)
662    ///
663    /// # Examples
664    ///
665    /// ```rust
666    /// use deribit_http::DeribitHttpClient;
667    ///
668    /// let client = DeribitHttpClient::new();
669    /// // let volatility = client.get_historical_volatility("BTC").await?;
670    /// // tracing::info!("Found {} volatility data points", volatility.len());
671    /// ```
672    pub async fn get_historical_volatility(
673        &self,
674        currency: &str,
675    ) -> Result<Vec<[f64; 2]>, HttpError> {
676        let query = format!("?currency={}", urlencoding::encode(currency));
677        self.public_get(GET_HISTORICAL_VOLATILITY, &query).await
678    }
679
680    /// Get mark price history
681    ///
682    /// Retrieves 5-minute historical mark price data for an instrument.
683    /// Mark prices are used for margin calculations and position valuations.
684    ///
685    /// **Note**: Currently, mark price history is available only for a subset of options
686    /// that participate in volatility index calculations. All other instruments,
687    /// including futures and perpetuals, will return an empty list.
688    ///
689    /// # Arguments
690    ///
691    /// * `instrument_name` - Unique instrument identifier (e.g., "BTC-25JUN21-50000-C")
692    /// * `start_timestamp` - The earliest timestamp to return results from (milliseconds since Unix epoch)
693    /// * `end_timestamp` - The most recent timestamp to return results from (milliseconds since Unix epoch)
694    ///
695    /// # Returns
696    ///
697    /// Returns a vector of `MarkPriceHistoryPoint` containing timestamp and mark price pairs.
698    ///
699    /// # Errors
700    ///
701    /// Returns `HttpError` if the request fails or the response cannot be parsed.
702    ///
703    /// # Examples
704    ///
705    /// ```rust
706    /// use deribit_http::DeribitHttpClient;
707    ///
708    /// let client = DeribitHttpClient::new();
709    /// // let history = client.get_mark_price_history(
710    /// //     "BTC-25JUN21-50000-C",
711    /// //     1609376800000,
712    /// //     1609376810000
713    /// // ).await?;
714    /// // for point in history {
715    /// //     println!("Time: {}, Mark Price: {}", point.timestamp, point.mark_price);
716    /// // }
717    /// ```
718    pub async fn get_mark_price_history(
719        &self,
720        instrument_name: &str,
721        start_timestamp: u64,
722        end_timestamp: u64,
723    ) -> Result<Vec<MarkPriceHistoryPoint>, HttpError> {
724        let query = format!(
725            "?instrument_name={}&start_timestamp={}&end_timestamp={}",
726            urlencoding::encode(instrument_name),
727            start_timestamp,
728            end_timestamp
729        );
730        self.public_get(GET_MARK_PRICE_HISTORY, &query).await
731    }
732
733    /// Get supported index names
734    ///
735    /// Retrieves the identifiers (names) of all supported price indexes.
736    /// Price indexes are reference prices used for mark price calculations,
737    /// settlement, and other market operations.
738    ///
739    /// # Arguments
740    ///
741    /// * `index_type` - Optional filter by index type: "all", "spot", or "derivative"
742    ///
743    /// # Returns
744    ///
745    /// Returns a vector of index name strings (e.g., "btc_eth", "btc_usdc").
746    ///
747    /// # Errors
748    ///
749    /// Returns `HttpError` if the request fails or the response cannot be parsed.
750    ///
751    /// # Examples
752    ///
753    /// ```rust
754    /// use deribit_http::DeribitHttpClient;
755    ///
756    /// let client = DeribitHttpClient::new();
757    /// // Get all index names
758    /// // let names = client.get_supported_index_names(None).await?;
759    /// // for name in names {
760    /// //     println!("Index: {}", name);
761    /// // }
762    /// //
763    /// // Get only spot indexes
764    /// // let spot_names = client.get_supported_index_names(Some("spot")).await?;
765    /// ```
766    pub async fn get_supported_index_names(
767        &self,
768        index_type: Option<&str>,
769    ) -> Result<Vec<String>, HttpError> {
770        let query = match index_type {
771            Some(t) => format!("?type={}", urlencoding::encode(t)),
772            None => String::new(),
773        };
774        self.public_get(GET_SUPPORTED_INDEX_NAMES, &query).await
775    }
776
777    /// Get supported index names with extended information
778    ///
779    /// Retrieves the identifiers (names) of all supported price indexes
780    /// along with combo trading availability flags.
781    ///
782    /// # Arguments
783    ///
784    /// * `index_type` - Optional filter by index type: "all", "spot", or "derivative"
785    ///
786    /// # Returns
787    ///
788    /// Returns a vector of `IndexNameInfo` containing index names and
789    /// optional combo trading flags.
790    ///
791    /// # Errors
792    ///
793    /// Returns `HttpError` if the request fails or the response cannot be parsed.
794    ///
795    /// # Examples
796    ///
797    /// ```rust
798    /// use deribit_http::DeribitHttpClient;
799    ///
800    /// let client = DeribitHttpClient::new();
801    /// // let indexes = client.get_supported_index_names_extended(None).await?;
802    /// // for idx in indexes {
803    /// //     println!("Index: {}, Future combo: {:?}", idx.name, idx.future_combo_enabled);
804    /// // }
805    /// ```
806    pub async fn get_supported_index_names_extended(
807        &self,
808        index_type: Option<&str>,
809    ) -> Result<Vec<IndexNameInfo>, HttpError> {
810        let query = match index_type {
811            Some(t) => format!("?type={}&extended=true", urlencoding::encode(t)),
812            None => "?extended=true".to_string(),
813        };
814        self.public_get(GET_SUPPORTED_INDEX_NAMES, &query).await
815    }
816
817    /// Get trade volumes
818    ///
819    /// Retrieves aggregated 24-hour trade volumes for different instrument types
820    /// and currencies. Block trades and Block RFQ trades are included.
821    /// Position moves are not included.
822    ///
823    /// # Arguments
824    ///
825    /// * `extended` - If true, include 7-day and 30-day volumes
826    ///
827    /// # Returns
828    ///
829    /// Returns a vector of `TradeVolume` with volume data per currency.
830    ///
831    /// # Errors
832    ///
833    /// Returns `HttpError` if the request fails or the response cannot be parsed.
834    ///
835    /// # Examples
836    ///
837    /// ```rust
838    /// use deribit_http::DeribitHttpClient;
839    ///
840    /// let client = DeribitHttpClient::new();
841    /// // Get basic 24h volumes
842    /// // let volumes = client.get_trade_volumes(false).await?;
843    /// // for vol in volumes {
844    /// //     println!("{}: futures={}, calls={}", vol.currency, vol.futures_volume, vol.calls_volume);
845    /// // }
846    /// //
847    /// // Get extended volumes (7d, 30d)
848    /// // let extended = client.get_trade_volumes(true).await?;
849    /// ```
850    pub async fn get_trade_volumes(&self, extended: bool) -> Result<Vec<TradeVolume>, HttpError> {
851        let query = if extended { "?extended=true" } else { "" };
852        self.public_get(GET_TRADE_VOLUMES, query).await
853    }
854
855    /// Get volatility index data
856    ///
857    /// Retrieves volatility index (VIX) chart data formatted as OHLC candles.
858    /// Useful for risk assessment and trading strategies.
859    ///
860    /// # Arguments
861    ///
862    /// * `currency` - Currency symbol (e.g., "BTC", "ETH")
863    /// * `start_timestamp` - Start timestamp in milliseconds since UNIX epoch
864    /// * `end_timestamp` - End timestamp in milliseconds since UNIX epoch
865    /// * `resolution` - Candle interval ("1", "60", "3600", "43200", "1D")
866    ///
867    /// # Returns
868    ///
869    /// Returns `VolatilityIndexData` with candles and optional continuation token.
870    ///
871    /// # Errors
872    ///
873    /// Returns `HttpError` if the request fails or the response cannot be parsed.
874    ///
875    /// # Examples
876    ///
877    /// ```rust
878    /// use deribit_http::DeribitHttpClient;
879    ///
880    /// let client = DeribitHttpClient::new();
881    /// // Get 1-hour VIX candles for BTC
882    /// // let vix_data = client.get_volatility_index_data(
883    /// //     "BTC",
884    /// //     1599373800000,
885    /// //     1599376800000,
886    /// //     "60"
887    /// // ).await?;
888    /// // for candle in &vix_data.data {
889    /// //     println!("ts={}, close={}", candle.timestamp, candle.close);
890    /// // }
891    /// ```
892    pub async fn get_volatility_index_data(
893        &self,
894        currency: &str,
895        start_timestamp: u64,
896        end_timestamp: u64,
897        resolution: &str,
898    ) -> Result<VolatilityIndexData, HttpError> {
899        let query = format!(
900            "?currency={}&start_timestamp={}&end_timestamp={}&resolution={}",
901            currency, start_timestamp, end_timestamp, resolution
902        );
903        self.public_get(GET_VOLATILITY_INDEX_DATA, &query).await
904    }
905
906    /// Get funding chart data
907    ///
908    /// Retrieves the list of the latest PERPETUAL funding chart points within a given time period.
909    ///
910    /// # Arguments
911    ///
912    /// * `instrument_name` - Instrument name
913    /// * `length` - Time period (8h, 24h, 1m)
914    ///
915    /// # Examples
916    ///
917    /// ```rust
918    /// use deribit_http::DeribitHttpClient;
919    ///
920    /// let client = DeribitHttpClient::new();
921    /// // let funding_data = client.get_funding_chart_data("BTC-PERPETUAL", "8h").await?;
922    /// // tracing::info!("Current interest: {}", funding_data.current_interest);
923    /// ```
924    pub async fn get_funding_chart_data(
925        &self,
926        instrument_name: &str,
927        length: &str,
928    ) -> Result<FundingChartData, HttpError> {
929        let query = format!(
930            "?instrument_name={}&length={}",
931            urlencoding::encode(instrument_name),
932            urlencoding::encode(length)
933        );
934        self.public_get(GET_FUNDING_CHART_DATA, &query).await
935    }
936
937    /// Get TradingView chart data
938    ///
939    /// Publicly available market data used to generate a TradingView candle chart.
940    ///
941    /// # Arguments
942    ///
943    /// * `instrument_name` - Instrument name
944    /// * `start_timestamp` - Start timestamp in milliseconds
945    /// * `end_timestamp` - End timestamp in milliseconds
946    /// * `resolution` - Chart resolution (1, 3, 5, 10, 15, 30, 60, 120, 180, 360)
947    ///
948    /// # Examples
949    ///
950    /// ```rust
951    /// use deribit_http::DeribitHttpClient;
952    ///
953    /// let client = DeribitHttpClient::new();
954    /// // let chart_data = client.get_tradingview_chart_data("BTC-PERPETUAL", 1554373800000, 1554376800000, "30").await?;
955    /// // tracing::info!("Chart status: {}", chart_data.status);
956    /// ```
957    pub async fn get_tradingview_chart_data(
958        &self,
959        instrument_name: &str,
960        start_timestamp: u64,
961        end_timestamp: u64,
962        resolution: &str,
963    ) -> Result<TradingViewChartData, HttpError> {
964        let query = format!(
965            "?instrument_name={}&start_timestamp={}&end_timestamp={}&resolution={}",
966            urlencoding::encode(instrument_name),
967            start_timestamp,
968            end_timestamp,
969            urlencoding::encode(resolution)
970        );
971        self.public_get(GET_TRADINGVIEW_CHART_DATA, &query).await
972    }
973
974    /// Get delivery prices
975    ///
976    /// Retrieves delivery prices for the given index.
977    /// This is a public endpoint that doesn't require authentication.
978    ///
979    /// # Arguments
980    ///
981    /// * `index_name` - Index identifier (e.g., "btc_usd", "eth_usd")
982    /// * `count` - Number of requested items (optional, default 20)
983    /// * `offset` - Offset for pagination (optional, default 0)
984    ///
985    /// # Examples
986    ///
987    /// ```rust
988    /// # use deribit_http::DeribitHttpClient;
989    /// # #[tokio::main]
990    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
991    /// let client = DeribitHttpClient::new(); // testnet
992    /// let delivery_prices = client.get_delivery_prices("btc_usd", Some(5), Some(0)).await?;
993    /// for price in delivery_prices.data {
994    ///     println!("Date: {} - Price: {}", price.date, price.delivery_price);
995    /// }
996    /// # Ok(())
997    /// # }
998    /// ```
999    pub async fn get_delivery_prices(
1000        &self,
1001        index_name: &str,
1002        count: Option<u32>,
1003        offset: Option<u32>,
1004    ) -> Result<DeliveryPricesResponse, HttpError> {
1005        let mut query = format!("?index_name={}", urlencoding::encode(index_name));
1006        if let Some(count) = count {
1007            query.push_str(&format!("&count={}", count));
1008        }
1009        if let Some(offset) = offset {
1010            query.push_str(&format!("&offset={}", offset));
1011        }
1012        self.public_get(GET_DELIVERY_PRICES, &query).await
1013    }
1014
1015    /// Get expirations
1016    ///
1017    /// Retrieves expirations for instruments. This method can be used to see instrument expirations.
1018    /// This is a public endpoint that doesn't require authentication.
1019    ///
1020    /// # Arguments
1021    ///
1022    /// * `currency` - The currency symbol (BTC, ETH, USDC, USDT, any, grouped)
1023    /// * `kind` - Instrument kind (future, option, any)
1024    /// * `currency_pair` - Currency pair identifier (optional)
1025    ///
1026    pub async fn get_expirations(
1027        &self,
1028        currency: &str,
1029        kind: &str,
1030        currency_pair: Option<&str>,
1031    ) -> Result<ExpirationsResponse, HttpError> {
1032        let mut query = format!(
1033            "?currency={}&kind={}",
1034            urlencoding::encode(currency),
1035            urlencoding::encode(kind)
1036        );
1037        if let Some(currency_pair) = currency_pair {
1038            query.push_str(&format!(
1039                "&currency_pair={}",
1040                urlencoding::encode(currency_pair)
1041            ));
1042        }
1043        self.public_get(GET_EXPIRATIONS, &query).await
1044    }
1045
1046    /// Get funding rate history
1047    ///
1048    /// Retrieves hourly historical interest rate for requested PERPETUAL instrument.
1049    /// This is a public endpoint that doesn't require authentication.
1050    ///
1051    /// # Arguments
1052    ///
1053    /// * `instrument_name` - Instrument name
1054    /// * `start_timestamp` - The earliest timestamp to return result from (milliseconds since UNIX epoch)
1055    /// * `end_timestamp` - The most recent timestamp to return result from (milliseconds since UNIX epoch)
1056    ///
1057    pub async fn get_funding_rate_history(
1058        &self,
1059        instrument_name: &str,
1060        start_timestamp: u64,
1061        end_timestamp: u64,
1062    ) -> Result<Vec<FundingRateData>, HttpError> {
1063        let query = format!(
1064            "?instrument_name={}&start_timestamp={}&end_timestamp={}",
1065            urlencoding::encode(instrument_name),
1066            start_timestamp,
1067            end_timestamp
1068        );
1069        self.public_get(GET_FUNDING_RATE_HISTORY, &query).await
1070    }
1071
1072    /// Get funding rate value
1073    ///
1074    /// Retrieves interest rate value for requested period. Applicable only for PERPETUAL instruments.
1075    /// This is a public endpoint that doesn't require authentication.
1076    ///
1077    /// # Arguments
1078    ///
1079    /// * `instrument_name` - Instrument name
1080    /// * `start_timestamp` - The earliest timestamp to return result from (milliseconds since UNIX epoch)
1081    /// * `end_timestamp` - The most recent timestamp to return result from (milliseconds since UNIX epoch)
1082    ///
1083    /// # Examples
1084    ///
1085    /// ```rust
1086    /// # use deribit_http::DeribitHttpClient;
1087    /// # #[tokio::main]
1088    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
1089    /// let client = DeribitHttpClient::new(); // testnet
1090    /// let funding_rate = client.get_funding_rate_value("BTC-PERPETUAL", 1569888000000, 1569974400000).await?;
1091    /// println!("Funding rate for period: {}", funding_rate);
1092    /// # Ok(())
1093    /// # }
1094    /// ```
1095    pub async fn get_funding_rate_value(
1096        &self,
1097        instrument_name: &str,
1098        start_timestamp: u64,
1099        end_timestamp: u64,
1100    ) -> Result<f64, HttpError> {
1101        let query = format!(
1102            "?instrument_name={}&start_timestamp={}&end_timestamp={}",
1103            urlencoding::encode(instrument_name),
1104            start_timestamp,
1105            end_timestamp
1106        );
1107        self.public_get(GET_FUNDING_RATE_VALUE, &query).await
1108    }
1109
1110    /// Get last settlements by currency
1111    ///
1112    /// Retrieves historical settlement, delivery and bankruptcy events coming from all instruments within a given currency.
1113    /// This is a public endpoint that doesn't require authentication.
1114    ///
1115    /// # Arguments
1116    ///
1117    /// * `currency` - The currency symbol (BTC, ETH, USDC, USDT, EURR)
1118    /// * `settlement_type` - Settlement type (settlement, delivery, bankruptcy) - optional
1119    /// * `count` - Number of requested items (optional, default 20)
1120    /// * `continuation` - Continuation token for pagination (optional)
1121    /// * `search_start_timestamp` - The latest timestamp to return result from (optional)
1122    ///
1123    pub async fn get_last_settlements_by_currency(
1124        &self,
1125        currency: &str,
1126        settlement_type: Option<&str>,
1127        count: Option<u32>,
1128        continuation: Option<&str>,
1129        search_start_timestamp: Option<u64>,
1130    ) -> Result<SettlementsResponse, HttpError> {
1131        let mut query = format!("?currency={}", urlencoding::encode(currency));
1132        if let Some(settlement_type) = settlement_type {
1133            query.push_str(&format!("&type={}", urlencoding::encode(settlement_type)));
1134        }
1135        if let Some(count) = count {
1136            query.push_str(&format!("&count={}", count));
1137        }
1138        if let Some(continuation) = continuation {
1139            query.push_str(&format!(
1140                "&continuation={}",
1141                urlencoding::encode(continuation)
1142            ));
1143        }
1144        if let Some(search_start_timestamp) = search_start_timestamp {
1145            query.push_str(&format!(
1146                "&search_start_timestamp={}",
1147                search_start_timestamp
1148            ));
1149        }
1150        self.public_get(GET_LAST_SETTLEMENTS_BY_CURRENCY, &query)
1151            .await
1152    }
1153
1154    /// Get last settlements by instrument
1155    ///
1156    /// Retrieves historical public settlement, delivery and bankruptcy events filtered by instrument name.
1157    /// This is a public endpoint that doesn't require authentication.
1158    ///
1159    /// # Arguments
1160    ///
1161    /// * `instrument_name` - Instrument name
1162    /// * `settlement_type` - Settlement type (settlement, delivery, bankruptcy) - optional
1163    /// * `count` - Number of requested items (optional, default 20)
1164    /// * `continuation` - Continuation token for pagination (optional)
1165    /// * `search_start_timestamp` - The latest timestamp to return result from (optional)
1166    ///
1167    pub async fn get_last_settlements_by_instrument(
1168        &self,
1169        instrument_name: &str,
1170        settlement_type: Option<&str>,
1171        count: Option<u32>,
1172        continuation: Option<&str>,
1173        search_start_timestamp: Option<u64>,
1174    ) -> Result<SettlementsResponse, HttpError> {
1175        let mut query = format!("?instrument_name={}", urlencoding::encode(instrument_name));
1176        if let Some(settlement_type) = settlement_type {
1177            query.push_str(&format!("&type={}", urlencoding::encode(settlement_type)));
1178        }
1179        if let Some(count) = count {
1180            query.push_str(&format!("&count={}", count));
1181        }
1182        if let Some(continuation) = continuation {
1183            query.push_str(&format!(
1184                "&continuation={}",
1185                urlencoding::encode(continuation)
1186            ));
1187        }
1188        if let Some(search_start_timestamp) = search_start_timestamp {
1189            query.push_str(&format!(
1190                "&search_start_timestamp={}",
1191                search_start_timestamp
1192            ));
1193        }
1194        self.public_get(GET_LAST_SETTLEMENTS_BY_INSTRUMENT, &query)
1195            .await
1196    }
1197
1198    /// Get last trades by currency
1199    ///
1200    /// Retrieves the latest trades that have occurred for instruments in a specific currency.
1201    /// This is a public endpoint that doesn't require authentication.
1202    ///
1203    /// # Arguments
1204    ///
1205    /// * `currency` - The currency symbol (BTC, ETH, USDC, USDT, EURR)
1206    /// * `kind` - Instrument kind (future, option, spot, etc.) - optional
1207    /// * `count` - Number of requested items (optional, default 10)
1208    /// * `include_old` - Include trades older than 7 days (optional)
1209    /// * `sorting` - Direction of results sorting (optional)
1210    ///
1211    /// # Examples
1212    ///
1213    /// ```rust
1214    /// # use deribit_http::DeribitHttpClient;
1215    /// # #[tokio::main]
1216    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
1217    /// let client = DeribitHttpClient::new(); // testnet
1218    /// let trades = client.get_last_trades_by_currency("BTC", Some("future"), Some(10), Some(false), Some("desc")).await?;
1219    /// for trade in trades.trades {
1220    ///     println!("Trade: {} {} at {}", trade.amount, trade.instrument_name, trade.price);
1221    /// }
1222    /// # Ok(())
1223    /// # }
1224    /// ```
1225    pub async fn get_last_trades_by_currency(
1226        &self,
1227        currency: &str,
1228        kind: Option<&str>,
1229        count: Option<u32>,
1230        include_old: Option<bool>,
1231        sorting: Option<&str>,
1232    ) -> Result<LastTradesResponse, HttpError> {
1233        let mut query = format!("?currency={}", urlencoding::encode(currency));
1234        if let Some(kind) = kind {
1235            query.push_str(&format!("&kind={}", urlencoding::encode(kind)));
1236        }
1237        if let Some(count) = count {
1238            query.push_str(&format!("&count={}", count));
1239        }
1240        if let Some(include_old) = include_old {
1241            query.push_str(&format!("&include_old={}", include_old));
1242        }
1243        if let Some(sorting) = sorting {
1244            query.push_str(&format!("&sorting={}", urlencoding::encode(sorting)));
1245        }
1246        self.public_get(GET_LAST_TRADES_BY_CURRENCY, &query).await
1247    }
1248
1249    /// Get last trades by currency and time
1250    ///
1251    /// Retrieves the latest trades that have occurred for instruments in a specific currency within a time range.
1252    /// This is a public endpoint that doesn't require authentication.
1253    ///
1254    /// # Arguments
1255    ///
1256    /// * `currency` - The currency symbol (BTC, ETH, USDC, USDT, EURR)
1257    /// * `start_timestamp` - The earliest timestamp to return result from (milliseconds since UNIX epoch)
1258    /// * `end_timestamp` - The most recent timestamp to return result from (milliseconds since UNIX epoch)
1259    /// * `kind` - Instrument kind (future, option, spot, etc.) - optional
1260    /// * `count` - Number of requested items (optional, default 10)
1261    /// * `include_old` - Include trades older than 7 days (optional)
1262    /// * `sorting` - Direction of results sorting (optional)
1263    ///
1264    /// # Examples
1265    ///
1266    /// ```rust
1267    /// # use deribit_http::DeribitHttpClient;
1268    /// # #[tokio::main]
1269    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
1270    /// let client = DeribitHttpClient::new(); // testnet
1271    /// let trades = client.get_last_trades_by_currency_and_time("BTC", 1569888000000, 1569974400000, Some("future"), Some(10), Some(false), Some("desc")).await?;
1272    /// for trade in trades.trades {
1273    ///     println!("Trade: {} {} at {}", trade.amount, trade.instrument_name, trade.price);
1274    /// }
1275    /// # Ok(())
1276    /// # }
1277    /// ```
1278    #[allow(clippy::too_many_arguments)]
1279    pub async fn get_last_trades_by_currency_and_time(
1280        &self,
1281        currency: &str,
1282        start_timestamp: u64,
1283        end_timestamp: u64,
1284        kind: Option<&str>,
1285        count: Option<u32>,
1286        include_old: Option<bool>,
1287        sorting: Option<&str>,
1288    ) -> Result<LastTradesResponse, HttpError> {
1289        let mut query = format!(
1290            "?currency={}&start_timestamp={}&end_timestamp={}",
1291            urlencoding::encode(currency),
1292            start_timestamp,
1293            end_timestamp
1294        );
1295        if let Some(kind) = kind {
1296            query.push_str(&format!("&kind={}", urlencoding::encode(kind)));
1297        }
1298        if let Some(count) = count {
1299            query.push_str(&format!("&count={}", count));
1300        }
1301        if let Some(include_old) = include_old {
1302            query.push_str(&format!("&include_old={}", include_old));
1303        }
1304        if let Some(sorting) = sorting {
1305            query.push_str(&format!("&sorting={}", urlencoding::encode(sorting)));
1306        }
1307        self.public_get(GET_LAST_TRADES_BY_CURRENCY_AND_TIME, &query)
1308            .await
1309    }
1310
1311    /// Get last trades by instrument and time
1312    ///
1313    /// Retrieves the latest trades that have occurred for a specific instrument within a time range.
1314    /// This is a public endpoint that doesn't require authentication.
1315    ///
1316    /// # Arguments
1317    ///
1318    /// * `instrument_name` - Instrument name
1319    /// * `start_timestamp` - The earliest timestamp to return result from (milliseconds since UNIX epoch)
1320    /// * `end_timestamp` - The most recent timestamp to return result from (milliseconds since UNIX epoch)
1321    /// * `count` - Number of requested items (optional, default 10)
1322    /// * `include_old` - Include trades older than 7 days (optional)
1323    /// * `sorting` - Direction of results sorting (optional)
1324    ///
1325    /// # Examples
1326    ///
1327    /// ```rust
1328    /// # use deribit_http::DeribitHttpClient;
1329    /// # #[tokio::main]
1330    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
1331    /// let client = DeribitHttpClient::new(); // testnet
1332    /// let trades = client.get_last_trades_by_instrument_and_time("BTC-PERPETUAL", 1569888000000, 1569974400000, Some(10), Some(false), Some("desc")).await?;
1333    /// for trade in trades.trades {
1334    ///     println!("Trade: {} at {} ({})", trade.amount, trade.price, trade.direction);
1335    /// }
1336    /// # Ok(())
1337    /// # }
1338    /// ```
1339    pub async fn get_last_trades_by_instrument_and_time(
1340        &self,
1341        instrument_name: &str,
1342        start_timestamp: u64,
1343        end_timestamp: u64,
1344        count: Option<u32>,
1345        include_old: Option<bool>,
1346        sorting: Option<&str>,
1347    ) -> Result<LastTradesResponse, HttpError> {
1348        let mut query = format!(
1349            "?instrument_name={}&start_timestamp={}&end_timestamp={}",
1350            urlencoding::encode(instrument_name),
1351            start_timestamp,
1352            end_timestamp
1353        );
1354        if let Some(count) = count {
1355            query.push_str(&format!("&count={}", count));
1356        }
1357        if let Some(include_old) = include_old {
1358            query.push_str(&format!("&include_old={}", include_old));
1359        }
1360        if let Some(sorting) = sorting {
1361            query.push_str(&format!("&sorting={}", urlencoding::encode(sorting)));
1362        }
1363        self.public_get(GET_LAST_TRADES_BY_INSTRUMENT_AND_TIME, &query)
1364            .await
1365    }
1366
1367    /// Get order book by instrument ID
1368    ///
1369    /// Retrieves the order book for the specified instrument by its ID.
1370    /// This is a public endpoint that doesn't require authentication.
1371    ///
1372    /// # Arguments
1373    ///
1374    /// * `instrument_id` - Instrument ID
1375    /// * `depth` - The number of entries to return for bid and ask order book entries (optional)
1376    ///
1377    /// # Examples
1378    ///
1379    /// ```rust,no_run
1380    /// # use deribit_http::DeribitHttpClient;
1381    /// # #[tokio::main]
1382    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
1383    /// let client = DeribitHttpClient::new(); // testnet
1384    /// let order_book = client.get_order_book_by_instrument_id(42, Some(5)).await?;
1385    /// println!("Order book for {}: {} bids, {} asks",
1386    ///          order_book.instrument_name,
1387    ///          order_book.bids.len(),
1388    ///          order_book.asks.len());
1389    /// # Ok(())
1390    /// # }
1391    /// ```
1392    pub async fn get_order_book_by_instrument_id(
1393        &self,
1394        instrument_id: u32,
1395        depth: Option<u32>,
1396    ) -> Result<OrderBook, HttpError> {
1397        let mut query = format!("?instrument_id={}", instrument_id);
1398        if let Some(depth) = depth {
1399            query.push_str(&format!("&depth={}", depth));
1400        }
1401        self.public_get(GET_ORDER_BOOK_BY_INSTRUMENT_ID, &query)
1402            .await
1403    }
1404
1405    /// Get platform announcements
1406    ///
1407    /// Retrieves announcements from the platform. Default start_timestamp is current time,
1408    /// count must be between 1 and 50 (default is 5).
1409    ///
1410    /// # Arguments
1411    ///
1412    /// * `count` - Number of announcements to retrieve (1-50, default 5)
1413    /// * `start_timestamp` - Optional timestamp to start from in milliseconds
1414    ///
1415    /// # Examples
1416    ///
1417    /// ```rust
1418    /// # use deribit_http::DeribitHttpClient;
1419    /// # #[tokio::main]
1420    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
1421    /// let client = DeribitHttpClient::new();
1422    /// let announcements = client.get_announcements(Some(10), None).await?;
1423    /// for announcement in announcements {
1424    ///     println!("Announcement: {}", announcement.title);
1425    /// }
1426    /// # Ok(())
1427    /// # }
1428    /// ```
1429    pub async fn get_announcements(
1430        &self,
1431        count: Option<u32>,
1432        start_timestamp: Option<u64>,
1433    ) -> Result<Vec<crate::model::Announcement>, HttpError> {
1434        let mut query_params = Vec::new();
1435        if let Some(count) = count {
1436            query_params.push(format!("count={}", count));
1437        }
1438        if let Some(ts) = start_timestamp {
1439            query_params.push(format!("start_timestamp={}", ts));
1440        }
1441        let query = if query_params.is_empty() {
1442            String::new()
1443        } else {
1444            format!("?{}", query_params.join("&"))
1445        };
1446        self.public_get(crate::constants::endpoints::GET_ANNOUNCEMENTS, &query)
1447            .await
1448    }
1449
1450    // ========================================================================
1451    // Combo Books Endpoints
1452    // ========================================================================
1453
1454    /// Get combo details by ID
1455    ///
1456    /// Retrieves information about a specific combo instrument.
1457    /// This is a public endpoint that doesn't require authentication.
1458    ///
1459    /// # Arguments
1460    ///
1461    /// * `combo_id` - The combo identifier (e.g., "BTC-FS-29APR22_PERP")
1462    ///
1463    /// # Errors
1464    ///
1465    /// Returns `HttpError` if the request fails or the response is invalid.
1466    ///
1467    /// # Examples
1468    ///
1469    /// ```rust,no_run
1470    /// use deribit_http::DeribitHttpClient;
1471    ///
1472    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1473    /// let client = DeribitHttpClient::new();
1474    /// let combo = client.get_combo_details("BTC-FS-29APR22_PERP").await?;
1475    /// println!("Combo {} has {} legs", combo.id, combo.legs.len());
1476    /// # Ok(())
1477    /// # }
1478    /// ```
1479    pub async fn get_combo_details(
1480        &self,
1481        combo_id: &str,
1482    ) -> Result<crate::model::Combo, HttpError> {
1483        let query = format!("?combo_id={}", urlencoding::encode(combo_id));
1484        self.public_get(GET_COMBO_DETAILS, &query).await
1485    }
1486
1487    /// Get combo IDs by currency
1488    ///
1489    /// Retrieves the list of available combo IDs for the specified currency.
1490    /// This is a public endpoint that doesn't require authentication.
1491    ///
1492    /// # Arguments
1493    ///
1494    /// * `currency` - The currency symbol (BTC, ETH, USDC, USDT, EURR)
1495    /// * `state` - Optional combo state filter (rfq, active, inactive)
1496    ///
1497    /// # Errors
1498    ///
1499    /// Returns `HttpError` if the request fails or the response is invalid.
1500    ///
1501    /// # Examples
1502    ///
1503    /// ```rust,no_run
1504    /// use deribit_http::DeribitHttpClient;
1505    ///
1506    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1507    /// let client = DeribitHttpClient::new();
1508    /// let combo_ids = client.get_combo_ids("BTC", Some("active")).await?;
1509    /// println!("Found {} active combos", combo_ids.len());
1510    /// # Ok(())
1511    /// # }
1512    /// ```
1513    pub async fn get_combo_ids(
1514        &self,
1515        currency: &str,
1516        state: Option<&str>,
1517    ) -> Result<Vec<String>, HttpError> {
1518        let mut query = format!("?currency={}", urlencoding::encode(currency));
1519        if let Some(s) = state {
1520            query.push_str(&format!("&state={}", urlencoding::encode(s)));
1521        }
1522        self.public_get(GET_COMBO_IDS, &query).await
1523    }
1524
1525    /// Get all active combos by currency
1526    ///
1527    /// Retrieves information about all active combo instruments for a currency.
1528    /// This is a public endpoint that doesn't require authentication.
1529    ///
1530    /// # Arguments
1531    ///
1532    /// * `currency` - The currency symbol (BTC, ETH, USDC, USDT, EURR, or "any" for all)
1533    ///
1534    /// # Errors
1535    ///
1536    /// Returns `HttpError` if the request fails or the response is invalid.
1537    ///
1538    /// # Examples
1539    ///
1540    /// ```rust,no_run
1541    /// use deribit_http::DeribitHttpClient;
1542    ///
1543    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1544    /// let client = DeribitHttpClient::new();
1545    /// let combos = client.get_combos("BTC").await?;
1546    /// for combo in combos {
1547    ///     println!("Combo: {} ({:?})", combo.id, combo.state);
1548    /// }
1549    /// # Ok(())
1550    /// # }
1551    /// ```
1552    pub async fn get_combos(&self, currency: &str) -> Result<Vec<crate::model::Combo>, HttpError> {
1553        let query = format!("?currency={}", urlencoding::encode(currency));
1554        self.public_get(GET_COMBOS, &query).await
1555    }
1556
1557    /// Retrieves a list of recent Block RFQ trades.
1558    ///
1559    /// This is a public method that provides market data about completed Block RFQ trades.
1560    /// Can be optionally filtered by currency.
1561    ///
1562    /// # Arguments
1563    ///
1564    /// * `currency` - Optional currency filter (e.g., "BTC", "ETH")
1565    /// * `count` - Optional number of trades to return (max 1000)
1566    /// * `continuation` - Optional continuation token for pagination
1567    ///
1568    /// # Returns
1569    ///
1570    /// Returns a `BlockRfqTradesResponse` containing the list of trades and pagination info.
1571    ///
1572    /// # Errors
1573    ///
1574    /// Returns `HttpError` if the request fails or the response cannot be parsed.
1575    ///
1576    /// # Examples
1577    ///
1578    /// ```no_run
1579    /// use deribit_http::DeribitHttpClient;
1580    ///
1581    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1582    /// let client = DeribitHttpClient::builder().testnet().build()?;
1583    /// let trades = client.get_block_rfq_trades(Some("BTC"), Some(20), None).await?;
1584    /// println!("Found {} Block RFQ trades", trades.len());
1585    /// # Ok(())
1586    /// # }
1587    /// ```
1588    pub async fn get_block_rfq_trades(
1589        &self,
1590        currency: Option<&str>,
1591        count: Option<u32>,
1592        continuation: Option<&str>,
1593    ) -> Result<crate::model::response::BlockRfqTradesResponse, HttpError> {
1594        let mut query_params: Vec<String> = Vec::new();
1595        if let Some(curr) = currency {
1596            query_params.push(format!("currency={}", curr));
1597        }
1598        if let Some(c) = count {
1599            query_params.push(format!("count={}", c));
1600        }
1601        if let Some(cont) = continuation {
1602            query_params.push(format!("continuation={}", cont));
1603        }
1604        let query = if query_params.is_empty() {
1605            String::new()
1606        } else {
1607            format!("?{}", query_params.join("&"))
1608        };
1609        self.public_get(crate::constants::endpoints::GET_BLOCK_RFQ_TRADES, &query)
1610            .await
1611    }
1612}