polygon_client/
rest.rs

1//! REST client for [polygon.io](https://polygon.io).
2//!
3//! # Authentication
4//!
5//! Use an [API key](https://polygon.io/dashboard/api-keys) to authenticate.
6//! This can be provided through the `auth_key` parameter to
7//! [`RESTClient::new()`] or through the `POLYGON_AUTH_KEY` environment variable.
8//!
9//! # Example
10//!
11//! ```
12//! use std::collections::HashMap;
13//!
14//! use polygon_client::rest::RESTClient;
15//!
16//! #[tokio::main]
17//! async fn main() {
18//!     let client = RESTClient::new(None, None);
19//!     let query_params = HashMap::new();
20//!     let resp = client.reference_tickers(&query_params)
21//!         .await
22//!         .expect("failed to query tickers");
23//!     for res in resp.results {
24//!         println!("ticker: {}", res.ticker);
25//!     }
26//! }
27//! ```
28use std::collections::HashMap;
29use std::env;
30
31use crate::types::*;
32
33static DEFAULT_API_URL: &str = "https://api.polygon.io";
34
35pub struct RESTClient {
36    /// The API key to use for requests.
37    pub auth_key: String,
38    /// The API URL to use for requests.
39    ///
40    /// The default API URL is <https://api.polygon.io>.
41    pub api_url: String,
42    client: reqwest::Client,
43}
44
45impl RESTClient {
46    /// Returns a new REST client.
47    ///
48    /// The `auth_key` parameter optionally provides the API key to use for
49    /// authentication. If `None` is provided, then the API key specified in the
50    /// `POLYGON_AUTH_KEY` environment variable is used.
51    ///
52    /// The `timeout` parameter optionally provides the duration to wait for a
53    /// response to a request.
54    ///
55    /// # Panics
56    ///
57    /// This function will panic if `auth_key` is `None` and the
58    /// `POLYGON_AUTH_KEY` environment variable is not set.
59    pub fn new(auth_key: Option<&str>, timeout: Option<core::time::Duration>) -> Self {
60        let api_url = match env::var("POLYGON_API_URL") {
61            Ok(v) => v,
62            _ => String::from(DEFAULT_API_URL),
63        };
64
65        let auth_key_actual = match auth_key {
66            Some(v) => String::from(v),
67            _ => match env::var("POLYGON_AUTH_KEY") {
68                Ok(v) => String::from(v),
69                _ => panic!("POLYGON_AUTH_KEY not set"),
70            },
71        };
72
73        let mut client = reqwest::ClientBuilder::new();
74
75        if timeout.is_some() {
76            client = client.timeout(timeout.unwrap());
77        }
78
79        RESTClient {
80            auth_key: auth_key_actual,
81            api_url: api_url,
82            client: client.build().unwrap(),
83        }
84    }
85
86    async fn send_request<RespType>(
87        &self,
88        uri: &str,
89        query_params: &HashMap<&str, &str>,
90    ) -> Result<RespType, reqwest::Error>
91    where
92        RespType: serde::de::DeserializeOwned,
93    {
94        let res = self
95            .client
96            .get(format!("{}{}", self.api_url, uri))
97            .bearer_auth(&self.auth_key)
98            .query(query_params)
99            .send()
100            .await;
101
102        match res {
103            Ok(res) => {
104                if res.status() == 200 {
105                    res.json::<RespType>().await
106                }
107                else {
108                    Err(res.error_for_status().err().unwrap())
109                }
110            }
111            Err(e) => {
112                Err(e)
113            }
114        }
115    }
116
117    //
118    // Reference APIs
119    //
120
121    /// Query all ticker symbols supported by polygon.io using the
122    /// [/v3/reference/tickers](https://polygon.io/docs/get_v3_reference_tickers_anchor)
123    /// API.
124    pub async fn reference_tickers(
125        &self,
126        query_params: &HashMap<&str, &str>,
127    ) -> Result<ReferenceTickersResponse, reqwest::Error> {
128        self.send_request::<ReferenceTickersResponse>("/v3/reference/tickers", query_params)
129            .await
130    }
131
132    /// Get a mapping of ticker types to their descriptive names using the
133    /// [/v2/reference/types](https://polygon.io/docs/get_v2_reference_types_anchor)
134    /// API.
135    pub async fn reference_ticker_types(
136        &self,
137        query_params: &HashMap<&str, &str>,
138    ) -> Result<ReferenceTickerTypesResponse, reqwest::Error> {
139        self.send_request::<ReferenceTickerTypesResponse>("/v2/reference/types", query_params)
140            .await
141    }
142
143    /// Get details for a ticker symbol's company/entity using the
144    /// [/v1/meta/symbols/{stocks_ticker}/company](https://polygon.io/docs/get_v1_meta_symbols__stocksTicker__company_anchor)
145    /// API.
146    pub async fn reference_ticker_details(
147        &self,
148        stocks_ticker: &str,
149        query_params: &HashMap<&str, &str>,
150    ) -> Result<ReferenceTickerDetailsResponse, reqwest::Error> {
151        let uri = format!("/v1/meta/symbols/{}/company", stocks_ticker);
152        self.send_request::<ReferenceTickerDetailsResponse>(&uri, query_params)
153            .await
154    }
155
156    /// Get details for a ticker symbol's company/entity using the
157    /// [/vX/reference/tickers/{stocks_ticker}](https://polygon.io/docs/get_vX_reference_tickers__ticker__anchor)
158    /// API.
159    pub async fn reference_ticker_details_vx(
160        &self,
161        stocks_ticker: &str,
162        query_params: &HashMap<&str, &str>,
163    ) -> Result<ReferenceTickerDetailsResponseVX, reqwest::Error> {
164        let uri = format!("/vX/reference/tickers/{}", stocks_ticker);
165        self.send_request::<ReferenceTickerDetailsResponseVX>(&uri, query_params)
166            .await
167    }
168
169    /// Get the most recent news articles related to a stock ticker symbol using
170    /// the [/v2/reference/news](https://polygon.io/docs/get_v2_reference_news_anchor) API.
171    pub async fn reference_ticker_news(
172        &self,
173        query_params: &HashMap<&str, &str>,
174    ) -> Result<ReferenceTickerNewsResponse, reqwest::Error> {
175        self.send_request::<ReferenceTickerNewsResponse>("/v2/reference/news", query_params)
176            .await
177    }
178
179    /// Get a list of markets that are currently supported by polygon.io using
180    /// the [/v2/reference/markets](https://polygon.io/docs/get_v2_reference_markets_anchor) API.
181    pub async fn reference_markets(
182        &self,
183        query_params: &HashMap<&str, &str>,
184    ) -> Result<ReferenceMarketsResponse, reqwest::Error> {
185        self.send_request::<ReferenceMarketsResponse>("/v2/reference/markets", query_params)
186            .await
187    }
188
189    /// Get a list of locales currently supported by polygon.io using the
190    /// [/v2/reference/locales](https://polygon.io/docs/get_v2_reference_locales_anchor) API.
191    pub async fn reference_locales(
192        &self,
193        query_params: &HashMap<&str, &str>,
194    ) -> Result<ReferenceLocalesResponse, reqwest::Error> {
195        self.send_request::<ReferenceLocalesResponse>("/v2/reference/locales", query_params)
196            .await
197    }
198
199    /// Get a list of historical stock splits for a ticker symbol using the
200    /// [/v2/reference/splits/{stocks_ticker}](https://polygon.io/docs/get_v2_reference_splits__stocksTicker__anchor) API.
201    pub async fn reference_stock_splits(
202        &self,
203        stocks_ticker: &str,
204        query_params: &HashMap<&str, &str>,
205    ) -> Result<ReferenceStockSplitsResponse, reqwest::Error> {
206        let uri = format!("/v2/reference/splits/{}", stocks_ticker);
207        self.send_request::<ReferenceStockSplitsResponse>(&uri, query_params)
208            .await
209    }
210
211    /// Get a list of historical dividends for a stock using the
212    /// [/v2/reference/dividends/{stocks_ticker}](https://polygon.io/docs/get_v2_reference_dividends__stocksTicker__anchor) API.
213    pub async fn reference_stock_dividends(
214        &self,
215        stocks_ticker: &str,
216        query_params: &HashMap<&str, &str>,
217    ) -> Result<ReferenceStockDividendsResponse, reqwest::Error> {
218        let uri = format!("/v2/reference/dividends/{}", stocks_ticker);
219        self.send_request::<ReferenceStockDividendsResponse>(&uri, query_params)
220            .await
221    }
222
223    /// Get historical financial data for a stock ticker using the
224    /// [/v2/reference/financials/{stocks_ticker}](https://polygon.io/docs/get_v2_reference_financials__stocksTicker__anchor) API.
225    pub async fn reference_stock_financials(
226        &self,
227        stocks_ticker: &str,
228        query_params: &HashMap<&str, &str>,
229    ) -> Result<ReferenceStockFinancialsResponse, reqwest::Error> {
230        let uri = format!("/v2/reference/financials/{}", stocks_ticker);
231        self.send_request::<ReferenceStockFinancialsResponse>(&uri, query_params)
232            .await
233    }
234
235    /// Get historical financial data for a stock ticker using the
236    /// [/vX/reference/financials](https://polygon.io/docs/get_vX_reference_financials_anchor) API.
237    pub async fn reference_stock_financials_vx(
238        &self,
239        query_params: &HashMap<&str, &str>,
240    ) -> Result<ReferenceStockFinancialsVXResponse, reqwest::Error> {
241        self.send_request::<ReferenceStockFinancialsVXResponse>(
242            "/vX/reference/financials",
243            query_params,
244        )
245        .await
246    }
247
248    /// Get upcoming market holidays and their open/close items using the
249    /// [/v1/marketstatus/upcoming](https://polygon.io/docs/get_v1_marketstatus_upcoming_anchor) API.
250    pub async fn reference_market_holidays(
251        &self,
252        query_params: &HashMap<&str, &str>,
253    ) -> Result<ReferenceMarketStatusUpcomingResponse, reqwest::Error> {
254        self.send_request::<ReferenceMarketStatusUpcomingResponse>(
255            "/v1/marketstatus/upcoming",
256            query_params,
257        )
258        .await
259    }
260
261    /// Get the current trading status of the exchanges and overall financial
262    /// markets using the [/v1/marketstatus/now](https://polygon.io/docs/get_v1_marketstatus_now_anchor) API.
263    pub async fn reference_market_status(
264        &self,
265        query_params: &HashMap<&str, &str>,
266    ) -> Result<ReferenceMarketStatusNowResponse, reqwest::Error> {
267        self.send_request::<ReferenceMarketStatusNowResponse>("/v1/marketstatus/now", query_params)
268            .await
269    }
270
271    //
272    // Stock equities APIs
273    //
274
275    /// Get a list of stock exchanges which are supported by polygon.io using
276    /// the [/v1/meta/exchanges](https://polygon.io/docs/get_v1_meta_exchanges_anchor) API.
277    pub async fn stock_equities_exchanges(
278        &self,
279        query_params: &HashMap<&str, &str>,
280    ) -> Result<StockEquitiesExchangesResponse, reqwest::Error> {
281        self.send_request::<StockEquitiesExchangesResponse>("/v1/meta/exchanges", query_params)
282            .await
283    }
284
285    /// Get a unified numerical mapping for conditions on trades and quotes
286    /// using the [/v1/meta/conditions/{tick_type}](https://polygon.io/docs/get_v1_meta_conditions__ticktype__anchor) API.
287    pub async fn stock_equities_condition_mappings(
288        &self,
289        tick_type: TickType,
290        query_params: &HashMap<&str, &str>,
291    ) -> Result<StockEquitiesConditionMappingsResponse, reqwest::Error> {
292        let uri = format!(
293            "/v1/meta/conditions/{}",
294            tick_type.to_string().to_lowercase()
295        );
296        self.send_request::<StockEquitiesConditionMappingsResponse>(&uri, query_params)
297            .await
298    }
299
300    /// Get the most recent trade for a given stock using the
301    /// [/v2/last/trade/{stocks_ticker}](https://polygon.io/docs/get_v2_last_trade__stocksTicker__anchor) API.
302    pub async fn stock_equities_historic_trades(
303        &self,
304        stocks_ticker: &str,
305        query_params: &HashMap<&str, &str>,
306    ) -> Result<StockEquitiesHistoricTradesResponse, reqwest::Error> {
307        let uri = format!("/v2/last/trade/{}", stocks_ticker);
308        self.send_request::<StockEquitiesHistoricTradesResponse>(&uri, query_params)
309            .await
310    }
311
312    /// Get the most recent NBBO quote tick for a given stock using the
313    /// [/v2/last/nbbo/{stocks_ticker}](https://polygon.io/docs/get_v2_last_nbbo__stocksTicker__anchor) API.
314    pub async fn stock_equities_last_quote_for_a_symbol(
315        &self,
316        stocks_ticker: &str,
317        query_params: &HashMap<&str, &str>,
318    ) -> Result<StockEquitiesLastQuoteForASymbolResponse, reqwest::Error> {
319        let uri = format!("/v2/last/nbbo/{}", stocks_ticker);
320        self.send_request::<StockEquitiesLastQuoteForASymbolResponse>(&uri, query_params)
321            .await
322    }
323
324    /// Get the open, close, and afterhours prices of a stock symbol on a
325    /// certain date using the [/v1/open-close/{stocks_ticker}/{date}](https://polygon.io/docs/get_v1_open-close__stocksTicker___date__anchor) API.
326    pub async fn stock_equities_daily_open_close(
327        &self,
328        stocks_ticker: &str,
329        date: &str,
330        query_params: &HashMap<&str, &str>,
331    ) -> Result<StockEquitiesDailyOpenCloseResponse, reqwest::Error> {
332        let uri = format!("/v1/open-close/{}/{}", stocks_ticker, date);
333        self.send_request::<StockEquitiesDailyOpenCloseResponse>(&uri, query_params)
334            .await
335    }
336
337    /// Get aggregate bars for a stock over a given date range in custom time
338    /// window sizes using the [/v2/aggs/ticker/{stocks_ticker}/range/{multiplier}/{timespan}/{from}/{to}](https://polygon.io/docs/get_v2_aggs_ticker__stocksTicker__range__multiplier___timespan___from___to__anchor) API.
339    pub async fn stock_equities_aggregates(
340        &self,
341        stocks_ticker: &str,
342        multiplier: u32,
343        timespan: &str,
344        from: &str,
345        to: &str,
346        query_params: &HashMap<&str, &str>,
347    ) -> Result<StockEquitiesAggregatesResponse, reqwest::Error> {
348        let uri = format!(
349            "/v2/aggs/ticker/{}/range/{}/{}/{}/{}",
350            stocks_ticker, multiplier, timespan, from, to
351        );
352        self.send_request::<StockEquitiesAggregatesResponse>(&uri, query_params)
353            .await
354    }
355
356    /// Get the daily open, high, low, and close for the entire stocks and
357    /// equities market using the [/v2/aggs/grouped/locale/{locale}/market/{market}/{date}](https://polygon.io/docs/get_v2_aggs_grouped_locale_us_market_stocks__date__anchor) API.
358    pub async fn stock_equities_grouped_daily(
359        &self,
360        locale: &str,
361        market: &str,
362        date: &str,
363        query_params: &HashMap<&str, &str>,
364    ) -> Result<StockEquitiesGroupedDailyResponse, reqwest::Error> {
365        let uri = format!(
366            "/v2/aggs/grouped/locale/{}/market/{}/{}",
367            locale, market, date
368        );
369        self.send_request::<StockEquitiesGroupedDailyResponse>(&uri, query_params)
370            .await
371    }
372
373    /// Get the previous day's open, high, low, and close for the specified
374    /// stock ticker using the [/v2/aggs/ticker/{stocks_ticker}/prev](https://polygon.io/docs/get_v2_aggs_ticker__stocksTicker__prev_anchor) API.
375    pub async fn stock_equities_previous_close(
376        &self,
377        stocks_ticker: &str,
378        query_params: &HashMap<&str, &str>,
379    ) -> Result<StockEquitiesPreviousCloseResponse, reqwest::Error> {
380        let uri = format!("/v2/aggs/ticker/{}/prev", stocks_ticker);
381        self.send_request::<StockEquitiesPreviousCloseResponse>(&uri, query_params)
382            .await
383    }
384
385    /// Get the current minute, day, and previous day's aggregate, as well as
386    /// the last trade and quote for all traded stock symbols using the [/v2/snapshot/locale/{locale}/markets/{market}/tickers](https://polygon.io/docs/get_v2_snapshot_locale_us_markets_stocks_tickers_anchor) API.
387    pub async fn stock_equities_snapshot_all_tickers(
388        &self,
389        locale: &str,
390        query_params: &HashMap<&str, &str>,
391    ) -> Result<StockEquitiesSnapshotAllTickersResponse, reqwest::Error> {
392        let uri = format!("/v2/snapshot/locale/{}/markets/stocks/tickers", locale);
393        self.send_request::<StockEquitiesSnapshotAllTickersResponse>(&uri, query_params)
394            .await
395    }
396
397    /// Get the current minute, day, and previous day's aggregate, as well as
398    /// the last trade and quote for a single traded stock ticker using the [/v2/snapshot/locale/{locale}/markets/stocks/tickers/{ticker}](https://polygon.io/docs/get_v2_snapshot_locale_us_markets_stocks_tickers__stocksTicker__anchor) API.
399    pub async fn stock_equities_snapshot_single_ticker(
400        &self,
401        locale: &str,
402        ticker: &str,
403        query_params: &HashMap<&str, &str>,
404    ) -> Result<StockEquitiesSnapshotAllTickersResponse, reqwest::Error> {
405        let uri = format!(
406            "/v2/snapshot/locale/{}/markets/stocks/tickers/{}",
407            locale, ticker
408        );
409        self.send_request::<StockEquitiesSnapshotAllTickersResponse>(&uri, query_params)
410            .await
411    }
412
413    /// Get the current top 20 gainers or losers of the day in the
414    /// stocks/equities markets using the [/v2/snapshot/locale/{locale}/markets/stocks/{direction}](https://polygon.io/docs/get_v2_snapshot_locale_us_markets_stocks__direction__anchor) API.
415    pub async fn stock_equities_snapshot_gainers_losers(
416        &self,
417        locale: &str,
418        direction: &str,
419        query_params: &HashMap<&str, &str>,
420    ) -> Result<StockEquitiesSnapshotGainersLosersResponse, reqwest::Error> {
421        let uri = format!(
422            "/v2/snapshot/locale/{}/markets/stocks/{}",
423            locale, direction
424        );
425        self.send_request::<StockEquitiesSnapshotGainersLosersResponse>(&uri, query_params)
426            .await
427    }
428
429    //
430    // Forex APIs
431    //
432
433    /// Get aggregate bars for a forex pair over a given date range in custom
434    /// time window sizes using the [/v2/aggs/ticker/{forexTicker}/range/{multiplier}/{timespan}/{from}/{to}](https://polygon.io/docs/get_v2_aggs_ticker__forexTicker__range__multiplier___timespan___from___to__anchor) API.
435    pub async fn forex_currencies_aggregates(
436        &self,
437        forex_ticker: &str,
438        multiplier: u32,
439        timespan: &str,
440        from: &str,
441        to: &str,
442        query_params: &HashMap<&str, &str>,
443    ) -> Result<ForexCurrenciesAggregatesResponse, reqwest::Error> {
444        let uri = format!(
445            "/v2/aggs/ticker/{}/range/{}/{}/{}/{}",
446            forex_ticker, multiplier, timespan, from, to
447        );
448        self.send_request::<ForexCurrenciesAggregatesResponse>(&uri, query_params)
449            .await
450    }
451
452    /// Get the daily open, high, low, and close for the entire forex markets
453    /// using the [/v2/aggs/grouped/locale/global/market/fx/{date}](https://polygon.io/docs/get_v2_aggs_grouped_locale_global_market_fx__date__anchor) API.
454    pub async fn forex_currencies_grouped_daily(
455        &self,
456        date: &str,
457        query_params: &HashMap<&str, &str>,
458    ) -> Result<ForexCurrenciesGroupedDailyResponse, reqwest::Error> {
459        let uri = format!("/v2/aggs/grouped/locale/global/market/fx/{}", date);
460        self.send_request::<ForexCurrenciesGroupedDailyResponse>(&uri, query_params)
461            .await
462    }
463
464    /// Get the previous day's open, high, low, and close for the specified
465    /// forex pair using the [/v2/aggs/ticker/{forex_ticker}/prev](https://polygon.io/docs/get_v2_aggs_ticker__forexTicker__prev_anchor) API.
466    pub async fn forex_currencies_previous_close(
467        &self,
468        forex_ticker: &str,
469        query_params: &HashMap<&str, &str>,
470    ) -> Result<ForexCurrenciesPreviousCloseResponse, reqwest::Error> {
471        let uri = format!("/v2/aggs/ticker/{}/prev", forex_ticker);
472        self.send_request::<ForexCurrenciesPreviousCloseResponse>(&uri, query_params)
473            .await
474    }
475
476    //
477    // Crypto APIs
478    //
479
480    /// Get a list of cryptocurrency exchanges which are supported by polygon.io
481    /// using the [/v1/meta/crypto-exchanges](https://polygon.io/docs/get_v1_meta_crypto-exchanges_anchor) API.
482    pub async fn crypto_crypto_exchanges(
483        &self,
484        query_params: &HashMap<&str, &str>,
485    ) -> Result<CryptoCryptoExchangesResponse, reqwest::Error> {
486        self.send_request::<CryptoCryptoExchangesResponse>(
487            "/v1/meta/crypto-exchanges",
488            query_params,
489        )
490        .await
491    }
492
493    /// Get the open and close prices of a cryptocurrency symbol on a certain day
494    /// using [/v1/open-close/crypto/{from}/{to}/{date}](https://polygon.io/docs/get_v1_open-close_crypto__from___to___date__anchor) API.
495    pub async fn crypto_daily_open_close(
496        &self,
497        from: &str,
498        to: &str,
499        date: &str,
500        query_params: &HashMap<&str, &str>,
501    ) -> Result<CryptoDailyOpenCloseResponse, reqwest::Error> {
502        let uri = format!("/v1/open-close/crypto/{}/{}/{}", from, to, date);
503        self.send_request::<CryptoDailyOpenCloseResponse>(&uri, query_params)
504            .await
505    }
506
507    /// Get aggregate bars for a cryptocurrency over a given date range in custom
508    /// time window sizes using the [/v2/aggs/ticker/{cryptoTicker}/range/{multiplier}/{timespan}/{from}/{to}](https://polygon.io/docs/get_v2_aggs_ticker__cryptoTicker__range__multiplier___timespan___from___to__anchor) API.
509    pub async fn crypto_aggregates(
510        &self,
511        crypto_ticker: &str,
512        multiplier: u32,
513        timespan: &str,
514        from: &str,
515        to: &str,
516        query_params: &HashMap<&str, &str>,
517    ) -> Result<CryptoAggregatesResponse, reqwest::Error> {
518        let uri = format!(
519            "/v2/aggs/ticker/{}/range/{}/{}/{}/{}",
520            crypto_ticker, multiplier, timespan, from, to
521        );
522        self.send_request::<CryptoAggregatesResponse>(&uri, query_params)
523            .await
524    }
525
526    /// Get the daily open, high, low, and close for the entire crypto markets
527    /// using the [/v2/aggs/grouped/locale/global/market/crypto/{date}](https://polygon.io/docs/get_v2_aggs_grouped_locale_global_market_crypto__date__anchor) API.
528    pub async fn crypto_grouped_daily(
529        &self,
530        date: &str,
531        query_params: &HashMap<&str, &str>,
532    ) -> Result<CryptoGroupedDailyResponse, reqwest::Error> {
533        let uri = format!("/v2/aggs/grouped/locale/global/market/crypto/{}", date);
534        self.send_request::<CryptoGroupedDailyResponse>(&uri, query_params)
535            .await
536    }
537
538    /// Get the previous day's open, high, low, and close for the specified
539    /// cryptocurrency using the [/v2/aggs/ticker/{crypto_ticker}/prev](https://polygon.io/docs/get_v2_aggs_ticker__cryptoTicker__prev_anchor) API.
540    pub async fn crypto_previous_close(
541        &self,
542        crypto_ticker: &str,
543        query_params: &HashMap<&str, &str>,
544    ) -> Result<CryptoPreviousCloseResponse, reqwest::Error> {
545        let uri = format!("/v2/aggs/ticker/{}/prev", crypto_ticker);
546        self.send_request::<CryptoPreviousCloseResponse>(&uri, query_params)
547            .await
548    }
549}
550
551#[cfg(test)]
552mod tests {
553    use crate::rest::RESTClient;
554    use crate::types::*;
555    use std::collections::HashMap;
556
557    #[test]
558    fn test_reference_tickers() {
559        let mut query_params = HashMap::new();
560        query_params.insert("ticker", "MSFT");
561        let resp =
562            tokio_test::block_on(RESTClient::new(None, None).reference_tickers(&query_params))
563                .unwrap();
564        assert_eq!(resp.status, "OK");
565        assert_eq!(resp.count, 1);
566        assert_eq!(resp.results[0].market, "stocks");
567        assert_eq!(resp.results[0].currency_name, "usd");
568    }
569
570    #[test]
571    fn test_reference_ticker_types() {
572        let query_params = HashMap::new();
573        let resp =
574            tokio_test::block_on(RESTClient::new(None, None).reference_ticker_types(&query_params))
575                .unwrap();
576        assert_eq!(resp.status, "OK");
577        assert_eq!(resp.results.types["CS"], "Common Stock");
578        assert_eq!(resp.results.index_types["INDEX"], "Index");
579    }
580
581    #[test]
582    fn test_reference_ticker_details() {
583        let query_params = HashMap::new();
584        let resp = tokio_test::block_on(
585            RESTClient::new(None, None).reference_ticker_details("MSFT", &query_params),
586        )
587        .unwrap();
588        assert_eq!(resp.country, "usa");
589        assert_eq!(resp.name, "Microsoft Corporation");
590        assert_eq!(resp.symbol, "MSFT");
591    }
592
593    #[test]
594    fn test_reference_ticker_details_vx() {
595        let query_params = HashMap::new();
596        let resp = tokio_test::block_on(
597            RESTClient::new(None, None).reference_ticker_details_vx("MSFT", &query_params),
598        )
599        .unwrap();
600        assert_eq!(resp.status, "OK");
601        assert_eq!(resp.results.ticker, "MSFT");
602        assert_eq!(resp.results.currency_name, "usd");
603    }
604
605    #[test]
606    fn test_reference_ticker_news() {
607        let query_params = HashMap::new();
608        let resp =
609            tokio_test::block_on(RESTClient::new(None, None).reference_ticker_news(&query_params))
610                .unwrap();
611        assert_eq!(resp.status, "OK");
612    }
613
614    #[test]
615    fn test_reference_markets() {
616        let query_params = HashMap::new();
617        let resp =
618            tokio_test::block_on(RESTClient::new(None, None).reference_markets(&query_params))
619                .unwrap();
620        assert_eq!(resp.status, "OK");
621        let bond = resp.results.iter().find(|x| x.market == "BONDS");
622        assert_eq!(bond.is_some(), true);
623        assert_eq!(bond.unwrap().desc, "Bonds");
624    }
625
626    #[test]
627    fn test_reference_locales() {
628        let query_params = HashMap::new();
629        let resp =
630            tokio_test::block_on(RESTClient::new(None, None).reference_locales(&query_params))
631                .unwrap();
632        assert_eq!(resp.status, "OK");
633        let bond = resp.results.iter().find(|x| x.locale == "US");
634        assert_eq!(bond.is_some(), true);
635        assert_eq!(bond.unwrap().name, "United States of America");
636    }
637
638    #[test]
639    fn test_reference_stock_splits() {
640        let query_params = HashMap::new();
641        let resp = tokio_test::block_on(
642            RESTClient::new(None, None).reference_stock_splits("MSFT", &query_params),
643        )
644        .unwrap();
645        assert_eq!(resp.status, "OK");
646        let bond = resp.results.iter().find(|x| x.ex_date == "1998-02-23");
647        assert_eq!(bond.is_some(), true);
648        assert_eq!(bond.unwrap().ratio, 0.5);
649    }
650
651    #[test]
652    fn test_reference_stock_dividends() {
653        let query_params = HashMap::new();
654        let resp = tokio_test::block_on(
655            RESTClient::new(None, None).reference_stock_dividends("MSFT", &query_params),
656        )
657        .unwrap();
658        assert_eq!(resp.status, "OK");
659        let bond = resp.results.iter().find(|x| x.ex_date == "2021-02-17");
660        assert_eq!(bond.is_some(), true);
661        assert_eq!(bond.unwrap().amount, 0.56);
662    }
663
664    #[test]
665    fn test_reference_stock_financials() {
666        let query_params = HashMap::new();
667        let resp = tokio_test::block_on(
668            RESTClient::new(None, None).reference_stock_financials("MSFT", &query_params),
669        )
670        .unwrap();
671        assert_eq!(resp.status, "OK");
672        let fin = resp.results.iter().find(|x| x.ticker == "MSFT");
673        assert_eq!(fin.is_some(), true);
674        let resp = tokio_test::block_on(
675            RESTClient::new(None, None).reference_stock_financials("AAPL", &query_params),
676        )
677        .unwrap();
678        let fin = resp.results.iter().find(|x| x.ticker == "AAPL");
679        assert_eq!(fin.is_some(), true);
680    }
681
682    #[test]
683    fn test_reference_stock_financials_vx() {
684        let mut query_params = HashMap::new();
685        query_params.insert("ticker", "MSFT");
686        let resp = tokio_test::block_on(
687            RESTClient::new(None, None).reference_stock_financials_vx(&query_params),
688        )
689        .unwrap();
690        assert_eq!(resp.status, "OK");
691        assert_eq!(resp.count, 1);
692        let result = resp.results.first().unwrap();
693        for v in &result.financials.balance_sheet {
694            println!("{} = true", v.0);
695        }
696        let income_statement = &result.financials.income_statement;
697        assert_eq!(income_statement.contains_key(FAC_REVENUES), true);
698        assert_eq!(
699            income_statement.get(FAC_REVENUES).unwrap().unit.is_some(),
700            true
701        );
702        assert_eq!(
703            income_statement
704                .get(FAC_REVENUES)
705                .unwrap()
706                .unit
707                .as_ref()
708                .unwrap(),
709            "USD"
710        );
711    }
712
713    #[test]
714    fn test_reference_market_holidays() {
715        let query_params = HashMap::new();
716        let resp = tokio_test::block_on(
717            RESTClient::new(None, None).reference_market_holidays(&query_params),
718        )
719        .unwrap();
720        assert_ne!(resp.len(), 0);
721    }
722
723    #[test]
724    fn test_reference_market_status() {
725        let query_params = HashMap::new();
726        let resp = tokio_test::block_on(
727            RESTClient::new(None, None).reference_market_status(&query_params),
728        )
729        .unwrap();
730        assert_ne!(resp.exchanges.len(), 0);
731    }
732
733    #[test]
734    fn test_stock_equities_exchanges() {
735        let query_params = HashMap::new();
736        let resp = tokio_test::block_on(
737            RESTClient::new(None, None).stock_equities_exchanges(&query_params),
738        )
739        .unwrap();
740        assert_ne!(resp.len(), 0);
741        let dji = resp
742            .iter()
743            .find(|x| x.code.is_some() && x.code.as_ref().unwrap() == "DJI");
744        assert_eq!(dji.is_some(), true);
745        assert_eq!(dji.unwrap().market, "index");
746    }
747
748    #[test]
749    fn test_stock_equities_condition_mappings() {
750        let query_params = HashMap::new();
751        let resp = tokio_test::block_on(
752            RESTClient::new(None, None)
753                .stock_equities_condition_mappings(TickType::Trades, &query_params),
754        )
755        .unwrap();
756        assert_ne!(resp.len(), 0);
757        let regular = resp.iter().find(|x| x.1 == "Regular");
758        assert_eq!(regular.is_some(), true);
759    }
760
761    #[test]
762    fn test_stock_equities_historic_trades() {
763        let query_params = HashMap::new();
764        let resp = tokio_test::block_on(
765            RESTClient::new(None, None).stock_equities_historic_trades("MSFT", &query_params),
766        )
767        .unwrap();
768        assert_eq!(resp.results.T.unwrap(), "MSFT");
769    }
770
771    #[test]
772    fn test_stock_equities_last_quote_for_a_symbol() {
773        let query_params = HashMap::new();
774        let resp = tokio_test::block_on(
775            RESTClient::new(None, None)
776                .stock_equities_last_quote_for_a_symbol("MSFT", &query_params),
777        )
778        .unwrap();
779        assert_eq!(resp.results.T.unwrap(), "MSFT");
780    }
781
782    #[test]
783    fn test_stock_equities_daily_open_close() {
784        let query_params = HashMap::new();
785        let resp =
786            tokio_test::block_on(RESTClient::new(None, None).stock_equities_daily_open_close(
787                "MSFT",
788                "2020-10-14",
789                &query_params,
790            ))
791            .unwrap();
792        assert_eq!(resp.symbol, "MSFT");
793        assert_eq!(resp.status, "OK");
794        assert_eq!(resp.open, 223f64);
795        assert_eq!(resp.high, 224.22);
796        assert_eq!(resp.low, 219.13);
797        assert_eq!(resp.close, 220.86);
798        assert_eq!(resp.volume, 23451713f64);
799        assert_eq!(resp.after_hours, 220.3);
800        assert_eq!(resp.pre_market, 224.03);
801    }
802
803    #[test]
804    fn test_stock_equities_aggregates() {
805        let query_params = HashMap::new();
806        let resp = tokio_test::block_on(RESTClient::new(None, None).stock_equities_aggregates(
807            "MSFT",
808            1,
809            "day",
810            "2020-10-14",
811            "2020-10-14",
812            &query_params,
813        ))
814        .unwrap();
815        assert_eq!(resp.ticker, "MSFT");
816        assert_eq!(resp.status, "OK");
817        assert_eq!(resp.query_count, 1);
818        assert_eq!(resp.results_count, 1);
819        let result = resp.results.first().unwrap();
820        assert_eq!(result.v, 23451713f64);
821        assert_eq!(result.vw.unwrap(), 221.41);
822        assert_eq!(result.o, 223f64);
823        assert_eq!(result.c, 220.86);
824        assert_eq!(result.h, 224.22);
825        assert_eq!(result.l, 219.13);
826        assert_eq!(result.t.unwrap(), 1602648000000);
827        assert_eq!(result.n.unwrap(), 244243f64);
828    }
829
830    #[test]
831    fn test_stock_equities_grouped_daily() {
832        let query_params = HashMap::new();
833        let resp = tokio_test::block_on(RESTClient::new(None, None).stock_equities_grouped_daily(
834            "us",
835            "stocks",
836            "2020-10-14",
837            &query_params,
838        ))
839        .unwrap();
840        assert_eq!(resp.status, "OK");
841        let msft = resp
842            .results
843            .iter()
844            .find(|x| x.T.is_some() && x.T.as_ref().unwrap() == "MSFT");
845        assert_eq!(msft.is_some(), true);
846        assert_eq!(msft.unwrap().vw.is_some(), true);
847        assert_eq!(msft.unwrap().vw.unwrap(), 221.41);
848        assert_eq!(msft.unwrap().o, 223f64);
849        assert_eq!(msft.unwrap().h, 224.22);
850        assert_eq!(msft.unwrap().l, 219.13);
851    }
852
853    #[test]
854    fn test_stock_equities_previous_close() {
855        let query_params = HashMap::new();
856        let resp = tokio_test::block_on(
857            RESTClient::new(None, None).stock_equities_previous_close("MSFT", &query_params),
858        )
859        .unwrap();
860        assert_eq!(resp.ticker, "MSFT");
861        assert_eq!(resp.status, "OK");
862        assert_eq!(resp.results_count, 1);
863        let result = resp.results.first();
864        assert_eq!(result.is_some(), true);
865        assert_eq!(result.unwrap().T.is_some(), true);
866        assert_eq!(result.unwrap().T.as_ref().unwrap(), "MSFT");
867    }
868
869    #[test]
870    fn test_stock_equities_snapshot_all_tickers() {
871        let query_params = HashMap::new();
872        let _resp = tokio_test::block_on(
873            RESTClient::new(None, None).stock_equities_snapshot_all_tickers("us", &query_params),
874        )
875        .unwrap();
876    }
877
878    #[test]
879    fn test_stock_equities_snapshot_gainers_losers() {
880        let query_params = HashMap::new();
881        let _resp = tokio_test::block_on(
882            RESTClient::new(None, None).stock_equities_snapshot_gainers_losers(
883                "us",
884                "gainers",
885                &query_params,
886            ),
887        )
888        .unwrap();
889    }
890
891    #[test]
892    fn test_crypto_crypto_exchanges() {
893        let query_params = HashMap::new();
894        let resp = tokio_test::block_on(
895            RESTClient::new(None, None).crypto_crypto_exchanges(&query_params),
896        )
897        .unwrap();
898        assert_ne!(resp.len(), 0);
899        let coinbase = resp.iter().find(|x| x.name == "Coinbase");
900        assert_eq!(coinbase.is_some(), true);
901    }
902
903    #[test]
904    fn test_forex_currencies_aggregates() {
905        let query_params = HashMap::new();
906        let resp = tokio_test::block_on(RESTClient::new(None, None).forex_currencies_aggregates(
907            "C:EURUSD",
908            1,
909            "day",
910            "2020-10-14",
911            "2020-10-14",
912            &query_params,
913        ))
914        .unwrap();
915        assert_eq!(resp.ticker, "C:EURUSD");
916        assert_eq!(resp.status, "OK");
917        assert_eq!(resp.query_count, 1);
918        assert_eq!(resp.results_count, 1);
919        let result = resp.results.first().unwrap();
920        assert_eq!(result.v, 211796f64);
921        assert_eq!(result.vw.unwrap(), 1.1748);
922        assert_eq!(result.o, 1.17439);
923        assert_eq!(result.c, 1.17496);
924        assert_eq!(result.h, 1.1771);
925        assert_eq!(result.l, 1.17198);
926        assert_eq!(result.t.unwrap(), 1602633600000);
927        assert_eq!(result.n.unwrap(), 211796f64);
928    }
929
930    #[test]
931    fn test_forex_currencies_grouped_daily() {
932        let query_params = HashMap::new();
933        let resp = tokio_test::block_on(
934            RESTClient::new(None, None).forex_currencies_grouped_daily("2020-10-14", &query_params),
935        )
936        .unwrap();
937        assert_eq!(resp.status, "OK");
938        let msft = resp
939            .results
940            .iter()
941            .find(|x| x.T.is_some() && x.T.as_ref().unwrap() == "C:EURMUR");
942        assert_eq!(msft.is_some(), true);
943        assert_eq!(msft.unwrap().vw.is_some(), true);
944        assert_eq!(msft.unwrap().vw.unwrap(), 45.2081);
945        assert_eq!(msft.unwrap().o, 45.37);
946        assert_eq!(msft.unwrap().h, 45.59);
947        assert_eq!(msft.unwrap().l, 44.83);
948    }
949
950    #[test]
951    fn test_forex_currencies_previous_close() {
952        let query_params = HashMap::new();
953        let resp = tokio_test::block_on(
954            RESTClient::new(None, None).forex_currencies_previous_close("C:EURUSD", &query_params),
955        )
956        .unwrap();
957        assert_eq!(resp.ticker, "C:EURUSD");
958        assert_eq!(resp.status, "OK");
959        assert_eq!(resp.results_count, 1);
960        let result = resp.results.first();
961        assert_eq!(result.is_some(), true);
962        assert_eq!(result.unwrap().T.is_some(), true);
963        assert_eq!(result.unwrap().T.as_ref().unwrap(), "C:EURUSD");
964    }
965
966    #[test]
967    fn test_crypto_daily_open_close() {
968        let mut query_params = HashMap::new();
969        query_params.insert("adjusted", "true");
970        let resp = tokio_test::block_on(RESTClient::new(None, None).crypto_daily_open_close(
971            "BTC",
972            "USD",
973            "2020-10-14",
974            &query_params,
975        ))
976        .unwrap();
977        assert_eq!(resp.symbol, "BTC-USD");
978        assert_eq!(resp.is_utc, true);
979        assert_eq!(resp.open, 11443f64);
980        assert_eq!(resp.close, 11427.7);
981    }
982
983    #[test]
984    fn test_crypto_aggregates() {
985        let query_params = HashMap::new();
986        let resp = tokio_test::block_on(RESTClient::new(None, None).crypto_aggregates(
987            "X:BTCUSD",
988            1,
989            "day",
990            "2020-10-14",
991            "2020-10-14",
992            &query_params,
993        ))
994        .unwrap();
995        assert_eq!(resp.ticker, "X:BTCUSD");
996        assert_eq!(resp.status, "OK");
997        assert_eq!(resp.query_count, 1);
998        assert_eq!(resp.results_count, 1);
999        let result = resp.results.first().unwrap();
1000        assert_eq!(result.vw.unwrap(), 11405.5019);
1001        assert_eq!(result.o, 11443f64);
1002        assert_eq!(result.c, 11427.7);
1003        assert_eq!(result.h, 11564f64);
1004        assert_eq!(result.l, 11284.27);
1005        assert_eq!(result.t.unwrap(), 1602633600000);
1006        assert_eq!(result.n.unwrap(), 142439f64);
1007    }
1008
1009    #[test]
1010    fn test_crypto_grouped_daily() {
1011        let query_params = HashMap::new();
1012        let resp = tokio_test::block_on(
1013            RESTClient::new(None, None).crypto_grouped_daily("2020-10-14", &query_params),
1014        )
1015        .unwrap();
1016        assert_eq!(resp.status, "OK");
1017        let msft = resp
1018            .results
1019            .iter()
1020            .find(|x| x.T.is_some() && x.T.as_ref().unwrap() == "X:LTCUSD");
1021        assert_eq!(msft.is_some(), true);
1022        assert_eq!(msft.unwrap().vw.is_some(), true);
1023        assert_eq!(msft.unwrap().vw.unwrap(), 50.1376);
1024        assert_eq!(msft.unwrap().o, 49.981);
1025        assert_eq!(msft.unwrap().h, 51.095);
1026        assert_eq!(msft.unwrap().l, 49.2427);
1027    }
1028
1029    #[test]
1030    fn test_crypto_previous_close() {
1031        let query_params = HashMap::new();
1032        let resp = tokio_test::block_on(
1033            RESTClient::new(None, None).crypto_previous_close("X:BTCUSD", &query_params),
1034        )
1035        .unwrap();
1036        assert_eq!(resp.ticker, "X:BTCUSD");
1037        assert_eq!(resp.status, "OK");
1038        assert_eq!(resp.results_count, 1);
1039        let result = resp.results.first();
1040        assert_eq!(result.is_some(), true);
1041        assert_eq!(result.unwrap().T.is_some(), true);
1042        assert_eq!(result.unwrap().T.as_ref().unwrap(), "X:BTCUSD");
1043    }
1044}