1use 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 pub auth_key: String,
38 pub api_url: String,
42 client: reqwest::Client,
43}
44
45impl RESTClient {
46 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}