Skip to main content

alpaca_data/stocks/
request.rs

1use crate::common::query::QueryWriter;
2use crate::transport::pagination::PaginatedRequest;
3
4use super::{Adjustment, AuctionFeed, Currency, DataFeed, Sort, Tape, TickType, TimeFrame};
5
6#[derive(Clone, Debug, Default)]
7pub struct BarsRequest {
8    pub symbols: Vec<String>,
9    pub timeframe: TimeFrame,
10    pub start: Option<String>,
11    pub end: Option<String>,
12    pub limit: Option<u32>,
13    pub adjustment: Option<Adjustment>,
14    pub feed: Option<DataFeed>,
15    pub sort: Option<Sort>,
16    pub asof: Option<String>,
17    pub currency: Option<Currency>,
18    pub page_token: Option<String>,
19}
20
21#[derive(Clone, Debug, Default)]
22pub struct BarsSingleRequest {
23    pub symbol: String,
24    pub timeframe: TimeFrame,
25    pub start: Option<String>,
26    pub end: Option<String>,
27    pub limit: Option<u32>,
28    pub adjustment: Option<Adjustment>,
29    pub feed: Option<DataFeed>,
30    pub sort: Option<Sort>,
31    pub asof: Option<String>,
32    pub currency: Option<Currency>,
33    pub page_token: Option<String>,
34}
35
36#[derive(Clone, Debug, Default)]
37pub struct AuctionsRequest {
38    pub symbols: Vec<String>,
39    pub start: Option<String>,
40    pub end: Option<String>,
41    pub limit: Option<u32>,
42    pub asof: Option<String>,
43    pub feed: Option<AuctionFeed>,
44    pub currency: Option<Currency>,
45    pub page_token: Option<String>,
46    pub sort: Option<Sort>,
47}
48
49#[derive(Clone, Debug, Default)]
50pub struct AuctionsSingleRequest {
51    pub symbol: String,
52    pub start: Option<String>,
53    pub end: Option<String>,
54    pub limit: Option<u32>,
55    pub asof: Option<String>,
56    pub feed: Option<AuctionFeed>,
57    pub currency: Option<Currency>,
58    pub page_token: Option<String>,
59    pub sort: Option<Sort>,
60}
61
62#[derive(Clone, Debug, Default)]
63pub struct QuotesRequest {
64    pub symbols: Vec<String>,
65    pub start: Option<String>,
66    pub end: Option<String>,
67    pub limit: Option<u32>,
68    pub feed: Option<DataFeed>,
69    pub sort: Option<Sort>,
70    pub asof: Option<String>,
71    pub currency: Option<Currency>,
72    pub page_token: Option<String>,
73}
74
75#[derive(Clone, Debug, Default)]
76pub struct QuotesSingleRequest {
77    pub symbol: String,
78    pub start: Option<String>,
79    pub end: Option<String>,
80    pub limit: Option<u32>,
81    pub feed: Option<DataFeed>,
82    pub sort: Option<Sort>,
83    pub asof: Option<String>,
84    pub currency: Option<Currency>,
85    pub page_token: Option<String>,
86}
87
88#[derive(Clone, Debug, Default)]
89pub struct TradesRequest {
90    pub symbols: Vec<String>,
91    pub start: Option<String>,
92    pub end: Option<String>,
93    pub limit: Option<u32>,
94    pub feed: Option<DataFeed>,
95    pub sort: Option<Sort>,
96    pub asof: Option<String>,
97    pub currency: Option<Currency>,
98    pub page_token: Option<String>,
99}
100
101#[derive(Clone, Debug, Default)]
102pub struct TradesSingleRequest {
103    pub symbol: String,
104    pub start: Option<String>,
105    pub end: Option<String>,
106    pub limit: Option<u32>,
107    pub feed: Option<DataFeed>,
108    pub sort: Option<Sort>,
109    pub asof: Option<String>,
110    pub currency: Option<Currency>,
111    pub page_token: Option<String>,
112}
113
114#[derive(Clone, Debug, Default)]
115pub struct LatestBarsRequest {
116    pub symbols: Vec<String>,
117    pub feed: Option<DataFeed>,
118    pub currency: Option<Currency>,
119}
120
121#[derive(Clone, Debug, Default)]
122pub struct LatestBarRequest {
123    pub symbol: String,
124    pub feed: Option<DataFeed>,
125    pub currency: Option<Currency>,
126}
127
128#[derive(Clone, Debug, Default)]
129pub struct LatestQuotesRequest {
130    pub symbols: Vec<String>,
131    pub feed: Option<DataFeed>,
132    pub currency: Option<Currency>,
133}
134
135#[derive(Clone, Debug, Default)]
136pub struct LatestQuoteRequest {
137    pub symbol: String,
138    pub feed: Option<DataFeed>,
139    pub currency: Option<Currency>,
140}
141
142#[derive(Clone, Debug, Default)]
143pub struct LatestTradesRequest {
144    pub symbols: Vec<String>,
145    pub feed: Option<DataFeed>,
146    pub currency: Option<Currency>,
147}
148
149#[derive(Clone, Debug, Default)]
150pub struct LatestTradeRequest {
151    pub symbol: String,
152    pub feed: Option<DataFeed>,
153    pub currency: Option<Currency>,
154}
155
156#[derive(Clone, Debug, Default)]
157pub struct SnapshotsRequest {
158    pub symbols: Vec<String>,
159    pub feed: Option<DataFeed>,
160    pub currency: Option<Currency>,
161}
162
163#[derive(Clone, Debug, Default)]
164pub struct SnapshotRequest {
165    pub symbol: String,
166    pub feed: Option<DataFeed>,
167    pub currency: Option<Currency>,
168}
169
170#[derive(Clone, Debug, Default)]
171pub struct ConditionCodesRequest {
172    pub ticktype: TickType,
173    pub tape: Tape,
174}
175
176impl BarsRequest {
177    pub(crate) fn to_query(self) -> Vec<(String, String)> {
178        let mut query = QueryWriter::default();
179        query.push_csv("symbols", self.symbols);
180        query.push_opt("timeframe", Some(self.timeframe));
181        query.push_opt("start", self.start);
182        query.push_opt("end", self.end);
183        query.push_opt("limit", self.limit);
184        query.push_opt("adjustment", self.adjustment);
185        query.push_opt("feed", self.feed);
186        query.push_opt("currency", self.currency);
187        query.push_opt("page_token", self.page_token);
188        query.push_opt("sort", self.sort);
189        query.push_opt("asof", self.asof);
190        query.finish()
191    }
192}
193
194impl BarsSingleRequest {
195    pub(crate) fn to_query(self) -> Vec<(String, String)> {
196        let mut query = QueryWriter::default();
197        query.push_opt("timeframe", Some(self.timeframe));
198        query.push_opt("start", self.start);
199        query.push_opt("end", self.end);
200        query.push_opt("limit", self.limit);
201        query.push_opt("adjustment", self.adjustment);
202        query.push_opt("feed", self.feed);
203        query.push_opt("currency", self.currency);
204        query.push_opt("page_token", self.page_token);
205        query.push_opt("sort", self.sort);
206        query.push_opt("asof", self.asof);
207        query.finish()
208    }
209}
210
211impl AuctionsRequest {
212    pub(crate) fn to_query(self) -> Vec<(String, String)> {
213        let mut query = QueryWriter::default();
214        query.push_csv("symbols", self.symbols);
215        query.push_opt("start", self.start);
216        query.push_opt("end", self.end);
217        query.push_opt("limit", self.limit);
218        query.push_opt("feed", self.feed);
219        query.push_opt("currency", self.currency);
220        query.push_opt("page_token", self.page_token);
221        query.push_opt("sort", self.sort);
222        query.push_opt("asof", self.asof);
223        query.finish()
224    }
225}
226
227impl AuctionsSingleRequest {
228    pub(crate) fn to_query(self) -> Vec<(String, String)> {
229        let mut query = QueryWriter::default();
230        query.push_opt("start", self.start);
231        query.push_opt("end", self.end);
232        query.push_opt("limit", self.limit);
233        query.push_opt("feed", self.feed);
234        query.push_opt("currency", self.currency);
235        query.push_opt("page_token", self.page_token);
236        query.push_opt("sort", self.sort);
237        query.push_opt("asof", self.asof);
238        query.finish()
239    }
240}
241
242impl QuotesRequest {
243    pub(crate) fn to_query(self) -> Vec<(String, String)> {
244        let mut query = QueryWriter::default();
245        query.push_csv("symbols", self.symbols);
246        query.push_opt("start", self.start);
247        query.push_opt("end", self.end);
248        query.push_opt("limit", self.limit);
249        query.push_opt("feed", self.feed);
250        query.push_opt("currency", self.currency);
251        query.push_opt("page_token", self.page_token);
252        query.push_opt("sort", self.sort);
253        query.push_opt("asof", self.asof);
254        query.finish()
255    }
256}
257
258impl QuotesSingleRequest {
259    pub(crate) fn to_query(self) -> Vec<(String, String)> {
260        let mut query = QueryWriter::default();
261        query.push_opt("start", self.start);
262        query.push_opt("end", self.end);
263        query.push_opt("limit", self.limit);
264        query.push_opt("feed", self.feed);
265        query.push_opt("currency", self.currency);
266        query.push_opt("page_token", self.page_token);
267        query.push_opt("sort", self.sort);
268        query.push_opt("asof", self.asof);
269        query.finish()
270    }
271}
272
273impl TradesRequest {
274    pub(crate) fn to_query(self) -> Vec<(String, String)> {
275        let mut query = QueryWriter::default();
276        query.push_csv("symbols", self.symbols);
277        query.push_opt("start", self.start);
278        query.push_opt("end", self.end);
279        query.push_opt("limit", self.limit);
280        query.push_opt("feed", self.feed);
281        query.push_opt("currency", self.currency);
282        query.push_opt("page_token", self.page_token);
283        query.push_opt("sort", self.sort);
284        query.push_opt("asof", self.asof);
285        query.finish()
286    }
287}
288
289impl TradesSingleRequest {
290    pub(crate) fn to_query(self) -> Vec<(String, String)> {
291        let mut query = QueryWriter::default();
292        query.push_opt("start", self.start);
293        query.push_opt("end", self.end);
294        query.push_opt("limit", self.limit);
295        query.push_opt("feed", self.feed);
296        query.push_opt("currency", self.currency);
297        query.push_opt("page_token", self.page_token);
298        query.push_opt("sort", self.sort);
299        query.push_opt("asof", self.asof);
300        query.finish()
301    }
302}
303
304impl LatestBarsRequest {
305    pub(crate) fn to_query(self) -> Vec<(String, String)> {
306        latest_batch_query(self.symbols, self.feed, self.currency)
307    }
308}
309
310impl LatestBarRequest {
311    pub(crate) fn to_query(self) -> Vec<(String, String)> {
312        latest_single_query(self.feed, self.currency)
313    }
314}
315
316impl LatestQuotesRequest {
317    pub(crate) fn to_query(self) -> Vec<(String, String)> {
318        latest_batch_query(self.symbols, self.feed, self.currency)
319    }
320}
321
322impl LatestQuoteRequest {
323    pub(crate) fn to_query(self) -> Vec<(String, String)> {
324        latest_single_query(self.feed, self.currency)
325    }
326}
327
328impl LatestTradesRequest {
329    pub(crate) fn to_query(self) -> Vec<(String, String)> {
330        latest_batch_query(self.symbols, self.feed, self.currency)
331    }
332}
333
334impl LatestTradeRequest {
335    pub(crate) fn to_query(self) -> Vec<(String, String)> {
336        latest_single_query(self.feed, self.currency)
337    }
338}
339
340impl SnapshotsRequest {
341    pub(crate) fn to_query(self) -> Vec<(String, String)> {
342        latest_batch_query(self.symbols, self.feed, self.currency)
343    }
344}
345
346impl SnapshotRequest {
347    pub(crate) fn to_query(self) -> Vec<(String, String)> {
348        latest_single_query(self.feed, self.currency)
349    }
350}
351
352impl ConditionCodesRequest {
353    pub(crate) fn to_query(self) -> Vec<(String, String)> {
354        let mut query = QueryWriter::default();
355        query.push_opt("tape", Some(self.tape));
356        query.finish()
357    }
358}
359
360impl PaginatedRequest for BarsSingleRequest {
361    fn with_page_token(&self, page_token: Option<String>) -> Self {
362        let mut next = self.clone();
363        next.page_token = page_token;
364        next
365    }
366}
367
368impl PaginatedRequest for QuotesSingleRequest {
369    fn with_page_token(&self, page_token: Option<String>) -> Self {
370        let mut next = self.clone();
371        next.page_token = page_token;
372        next
373    }
374}
375
376impl PaginatedRequest for AuctionsSingleRequest {
377    fn with_page_token(&self, page_token: Option<String>) -> Self {
378        let mut next = self.clone();
379        next.page_token = page_token;
380        next
381    }
382}
383
384fn latest_batch_query(
385    symbols: Vec<String>,
386    feed: Option<DataFeed>,
387    currency: Option<Currency>,
388) -> Vec<(String, String)> {
389    let mut query = QueryWriter::default();
390    query.push_csv("symbols", symbols);
391    query.push_opt("feed", feed);
392    query.push_opt("currency", currency);
393    query.finish()
394}
395
396fn latest_single_query(
397    feed: Option<DataFeed>,
398    currency: Option<Currency>,
399) -> Vec<(String, String)> {
400    let mut query = QueryWriter::default();
401    query.push_opt("feed", feed);
402    query.push_opt("currency", currency);
403    query.finish()
404}
405
406impl PaginatedRequest for TradesSingleRequest {
407    fn with_page_token(&self, page_token: Option<String>) -> Self {
408        let mut next = self.clone();
409        next.page_token = page_token;
410        next
411    }
412}
413
414impl PaginatedRequest for BarsRequest {
415    fn with_page_token(&self, page_token: Option<String>) -> Self {
416        let mut next = self.clone();
417        next.page_token = page_token;
418        next
419    }
420}
421
422impl PaginatedRequest for AuctionsRequest {
423    fn with_page_token(&self, page_token: Option<String>) -> Self {
424        let mut next = self.clone();
425        next.page_token = page_token;
426        next
427    }
428}
429
430impl PaginatedRequest for QuotesRequest {
431    fn with_page_token(&self, page_token: Option<String>) -> Self {
432        let mut next = self.clone();
433        next.page_token = page_token;
434        next
435    }
436}
437
438impl PaginatedRequest for TradesRequest {
439    fn with_page_token(&self, page_token: Option<String>) -> Self {
440        let mut next = self.clone();
441        next.page_token = page_token;
442        next
443    }
444}
445
446#[cfg(test)]
447mod tests {
448    use super::*;
449
450    #[test]
451    fn stocks_data_feed_serializes_to_official_strings() {
452        assert_eq!(DataFeed::DelayedSip.to_string(), "delayed_sip");
453        assert_eq!(DataFeed::Iex.to_string(), "iex");
454        assert_eq!(DataFeed::Otc.to_string(), "otc");
455        assert_eq!(DataFeed::Sip.to_string(), "sip");
456        assert_eq!(DataFeed::Boats.to_string(), "boats");
457        assert_eq!(DataFeed::Overnight.to_string(), "overnight");
458    }
459
460    #[test]
461    fn stocks_adjustment_serializes_to_official_strings() {
462        assert_eq!(Adjustment::raw().to_string(), "raw");
463        assert_eq!(Adjustment::split().to_string(), "split");
464        assert_eq!(Adjustment::dividend().to_string(), "dividend");
465        assert_eq!(Adjustment::spin_off().to_string(), "spin-off");
466        assert_eq!(Adjustment::all().to_string(), "all");
467        assert_eq!(
468            Adjustment::from("split,dividend").to_string(),
469            "split,dividend"
470        );
471    }
472
473    #[test]
474    fn stocks_timeframe_serializes_to_official_strings() {
475        assert_eq!(TimeFrame::from("1Min").to_string(), "1Min");
476        assert_eq!(TimeFrame::from("5Min").to_string(), "5Min");
477        assert_eq!(TimeFrame::from("1Day").to_string(), "1Day");
478        assert_eq!(TimeFrame::from("1Week").to_string(), "1Week");
479        assert_eq!(TimeFrame::from("3Month").to_string(), "3Month");
480    }
481
482    #[test]
483    fn bars_request_serializes_official_query_words() {
484        let request = BarsRequest {
485            symbols: vec!["AAPL".into(), "MSFT".into()],
486            timeframe: TimeFrame::from("1Day"),
487            start: Some("2024-03-01T00:00:00Z".into()),
488            end: Some("2024-03-05T00:00:00Z".into()),
489            limit: Some(50),
490            adjustment: Some(Adjustment::from("split,dividend")),
491            feed: Some(DataFeed::Boats),
492            sort: Some(Sort::Desc),
493            asof: Some("2024-03-04".into()),
494            currency: Some(Currency::from("USD")),
495            page_token: Some("page-123".into()),
496        };
497
498        assert_eq!(
499            request.to_query(),
500            vec![
501                ("symbols".to_string(), "AAPL,MSFT".to_string()),
502                ("timeframe".to_string(), "1Day".to_string()),
503                ("start".to_string(), "2024-03-01T00:00:00Z".to_string()),
504                ("end".to_string(), "2024-03-05T00:00:00Z".to_string()),
505                ("limit".to_string(), "50".to_string()),
506                ("adjustment".to_string(), "split,dividend".to_string()),
507                ("feed".to_string(), "boats".to_string()),
508                ("currency".to_string(), "USD".to_string()),
509                ("page_token".to_string(), "page-123".to_string()),
510                ("sort".to_string(), "desc".to_string()),
511                ("asof".to_string(), "2024-03-04".to_string()),
512            ]
513        );
514    }
515
516    #[test]
517    fn bars_single_request_serializes_official_query_words() {
518        let request = BarsSingleRequest {
519            symbol: "AAPL".into(),
520            timeframe: TimeFrame::from("1Day"),
521            start: Some("2024-03-01T00:00:00Z".into()),
522            end: Some("2024-03-05T00:00:00Z".into()),
523            limit: Some(50),
524            adjustment: Some(Adjustment::from("split,dividend")),
525            feed: Some(DataFeed::Boats),
526            sort: Some(Sort::Desc),
527            asof: Some("2024-03-04".into()),
528            currency: Some(Currency::from("USD")),
529            page_token: Some("page-123".into()),
530        };
531
532        assert_eq!(
533            request.to_query(),
534            vec![
535                ("timeframe".to_string(), "1Day".to_string()),
536                ("start".to_string(), "2024-03-01T00:00:00Z".to_string()),
537                ("end".to_string(), "2024-03-05T00:00:00Z".to_string()),
538                ("limit".to_string(), "50".to_string()),
539                ("adjustment".to_string(), "split,dividend".to_string()),
540                ("feed".to_string(), "boats".to_string()),
541                ("currency".to_string(), "USD".to_string()),
542                ("page_token".to_string(), "page-123".to_string()),
543                ("sort".to_string(), "desc".to_string()),
544                ("asof".to_string(), "2024-03-04".to_string()),
545            ]
546        );
547    }
548
549    #[test]
550    fn stocks_auction_feed_serializes_to_official_strings() {
551        assert_eq!(AuctionFeed::Sip.to_string(), "sip");
552    }
553
554    #[test]
555    fn auctions_request_serializes_official_query_words() {
556        let request = AuctionsRequest {
557            symbols: vec!["AAPL".into(), "MSFT".into()],
558            start: Some("2024-03-01T00:00:00Z".into()),
559            end: Some("2024-03-05T00:00:00Z".into()),
560            limit: Some(10),
561            asof: Some("2024-03-04".into()),
562            feed: Some(AuctionFeed::Sip),
563            currency: Some(Currency::from("USD")),
564            page_token: Some("page-auctions".into()),
565            sort: Some(Sort::Asc),
566        };
567
568        assert_eq!(
569            request.to_query(),
570            vec![
571                ("symbols".to_string(), "AAPL,MSFT".to_string()),
572                ("start".to_string(), "2024-03-01T00:00:00Z".to_string()),
573                ("end".to_string(), "2024-03-05T00:00:00Z".to_string()),
574                ("limit".to_string(), "10".to_string()),
575                ("feed".to_string(), "sip".to_string()),
576                ("currency".to_string(), "USD".to_string()),
577                ("page_token".to_string(), "page-auctions".to_string()),
578                ("sort".to_string(), "asc".to_string()),
579                ("asof".to_string(), "2024-03-04".to_string()),
580            ]
581        );
582    }
583
584    #[test]
585    fn auctions_single_request_serializes_official_query_words() {
586        let request = AuctionsSingleRequest {
587            symbol: "AAPL".into(),
588            start: Some("2024-03-01T00:00:00Z".into()),
589            end: Some("2024-03-05T00:00:00Z".into()),
590            limit: Some(10),
591            asof: Some("2024-03-04".into()),
592            feed: Some(AuctionFeed::Sip),
593            currency: Some(Currency::from("USD")),
594            page_token: Some("page-auctions-single".into()),
595            sort: Some(Sort::Desc),
596        };
597
598        assert_eq!(
599            request.to_query(),
600            vec![
601                ("start".to_string(), "2024-03-01T00:00:00Z".to_string()),
602                ("end".to_string(), "2024-03-05T00:00:00Z".to_string()),
603                ("limit".to_string(), "10".to_string()),
604                ("feed".to_string(), "sip".to_string()),
605                ("currency".to_string(), "USD".to_string()),
606                ("page_token".to_string(), "page-auctions-single".to_string()),
607                ("sort".to_string(), "desc".to_string()),
608                ("asof".to_string(), "2024-03-04".to_string()),
609            ]
610        );
611    }
612
613    #[test]
614    fn quotes_request_serializes_official_query_words() {
615        let request = QuotesRequest {
616            symbols: vec!["AAPL".into(), "MSFT".into()],
617            start: Some("2024-03-01T00:00:00Z".into()),
618            end: Some("2024-03-05T00:00:00Z".into()),
619            limit: Some(25),
620            feed: Some(DataFeed::Iex),
621            sort: Some(Sort::Asc),
622            asof: Some("2024-03-04".into()),
623            currency: Some(Currency::from("USD")),
624            page_token: Some("page-456".into()),
625        };
626
627        assert_eq!(
628            request.to_query(),
629            vec![
630                ("symbols".to_string(), "AAPL,MSFT".to_string()),
631                ("start".to_string(), "2024-03-01T00:00:00Z".to_string()),
632                ("end".to_string(), "2024-03-05T00:00:00Z".to_string()),
633                ("limit".to_string(), "25".to_string()),
634                ("feed".to_string(), "iex".to_string()),
635                ("currency".to_string(), "USD".to_string()),
636                ("page_token".to_string(), "page-456".to_string()),
637                ("sort".to_string(), "asc".to_string()),
638                ("asof".to_string(), "2024-03-04".to_string()),
639            ]
640        );
641    }
642
643    #[test]
644    fn quotes_single_request_serializes_official_query_words() {
645        let request = QuotesSingleRequest {
646            symbol: "AAPL".into(),
647            start: Some("2024-03-01T00:00:00Z".into()),
648            end: Some("2024-03-05T00:00:00Z".into()),
649            limit: Some(25),
650            feed: Some(DataFeed::Iex),
651            sort: Some(Sort::Asc),
652            asof: Some("2024-03-04".into()),
653            currency: Some(Currency::from("USD")),
654            page_token: Some("page-456".into()),
655        };
656
657        assert_eq!(
658            request.to_query(),
659            vec![
660                ("start".to_string(), "2024-03-01T00:00:00Z".to_string()),
661                ("end".to_string(), "2024-03-05T00:00:00Z".to_string()),
662                ("limit".to_string(), "25".to_string()),
663                ("feed".to_string(), "iex".to_string()),
664                ("currency".to_string(), "USD".to_string()),
665                ("page_token".to_string(), "page-456".to_string()),
666                ("sort".to_string(), "asc".to_string()),
667                ("asof".to_string(), "2024-03-04".to_string()),
668            ]
669        );
670    }
671
672    #[test]
673    fn trades_request_serializes_official_query_words() {
674        let request = TradesRequest {
675            symbols: vec!["AAPL".into(), "MSFT".into()],
676            start: Some("2024-03-01T00:00:00Z".into()),
677            end: Some("2024-03-05T00:00:00Z".into()),
678            limit: Some(10),
679            feed: Some(DataFeed::Sip),
680            sort: Some(Sort::Desc),
681            asof: Some("2024-03-04".into()),
682            currency: Some(Currency::from("USD")),
683            page_token: Some("page-789".into()),
684        };
685
686        assert_eq!(
687            request.to_query(),
688            vec![
689                ("symbols".to_string(), "AAPL,MSFT".to_string()),
690                ("start".to_string(), "2024-03-01T00:00:00Z".to_string()),
691                ("end".to_string(), "2024-03-05T00:00:00Z".to_string()),
692                ("limit".to_string(), "10".to_string()),
693                ("feed".to_string(), "sip".to_string()),
694                ("currency".to_string(), "USD".to_string()),
695                ("page_token".to_string(), "page-789".to_string()),
696                ("sort".to_string(), "desc".to_string()),
697                ("asof".to_string(), "2024-03-04".to_string()),
698            ]
699        );
700    }
701
702    #[test]
703    fn trades_single_request_serializes_official_query_words() {
704        let request = TradesSingleRequest {
705            symbol: "AAPL".into(),
706            start: Some("2024-03-01T00:00:00Z".into()),
707            end: Some("2024-03-05T00:00:00Z".into()),
708            limit: Some(10),
709            feed: Some(DataFeed::Sip),
710            sort: Some(Sort::Desc),
711            asof: Some("2024-03-04".into()),
712            currency: Some(Currency::from("USD")),
713            page_token: Some("page-789".into()),
714        };
715
716        assert_eq!(
717            request.to_query(),
718            vec![
719                ("start".to_string(), "2024-03-01T00:00:00Z".to_string()),
720                ("end".to_string(), "2024-03-05T00:00:00Z".to_string()),
721                ("limit".to_string(), "10".to_string()),
722                ("feed".to_string(), "sip".to_string()),
723                ("currency".to_string(), "USD".to_string()),
724                ("page_token".to_string(), "page-789".to_string()),
725                ("sort".to_string(), "desc".to_string()),
726                ("asof".to_string(), "2024-03-04".to_string()),
727            ]
728        );
729    }
730
731    #[test]
732    fn latest_batch_requests_serialize_official_query_words() {
733        let bars = LatestBarsRequest {
734            symbols: vec!["AAPL".into(), "MSFT".into()],
735            feed: Some(DataFeed::DelayedSip),
736            currency: Some(Currency::from("USD")),
737        };
738
739        assert_eq!(
740            bars.to_query(),
741            vec![
742                ("symbols".to_string(), "AAPL,MSFT".to_string()),
743                ("feed".to_string(), "delayed_sip".to_string()),
744                ("currency".to_string(), "USD".to_string()),
745            ]
746        );
747
748        let trades = LatestTradesRequest {
749            symbols: vec!["AAPL".into(), "MSFT".into()],
750            feed: Some(DataFeed::Iex),
751            currency: Some(Currency::from("USD")),
752        };
753
754        assert_eq!(
755            trades.to_query(),
756            vec![
757                ("symbols".to_string(), "AAPL,MSFT".to_string()),
758                ("feed".to_string(), "iex".to_string()),
759                ("currency".to_string(), "USD".to_string()),
760            ]
761        );
762    }
763
764    #[test]
765    fn latest_single_requests_serialize_official_query_words() {
766        let bar = LatestBarRequest {
767            symbol: "AAPL".into(),
768            feed: Some(DataFeed::Sip),
769            currency: Some(Currency::from("USD")),
770        };
771
772        assert_eq!(
773            bar.to_query(),
774            vec![
775                ("feed".to_string(), "sip".to_string()),
776                ("currency".to_string(), "USD".to_string()),
777            ]
778        );
779
780        let trade = LatestTradeRequest {
781            symbol: "AAPL".into(),
782            feed: Some(DataFeed::Boats),
783            currency: Some(Currency::from("USD")),
784        };
785
786        assert_eq!(
787            trade.to_query(),
788            vec![
789                ("feed".to_string(), "boats".to_string()),
790                ("currency".to_string(), "USD".to_string()),
791            ]
792        );
793    }
794
795    #[test]
796    fn snapshot_requests_serialize_official_query_words() {
797        let batch = SnapshotsRequest {
798            symbols: vec!["AAPL".into(), "MSFT".into()],
799            feed: Some(DataFeed::Overnight),
800            currency: Some(Currency::from("USD")),
801        };
802
803        assert_eq!(
804            batch.to_query(),
805            vec![
806                ("symbols".to_string(), "AAPL,MSFT".to_string()),
807                ("feed".to_string(), "overnight".to_string()),
808                ("currency".to_string(), "USD".to_string()),
809            ]
810        );
811
812        let single = SnapshotRequest {
813            symbol: "AAPL".into(),
814            feed: Some(DataFeed::Otc),
815            currency: Some(Currency::from("USD")),
816        };
817
818        assert_eq!(
819            single.to_query(),
820            vec![
821                ("feed".to_string(), "otc".to_string()),
822                ("currency".to_string(), "USD".to_string()),
823            ]
824        );
825    }
826
827    #[test]
828    fn stocks_ticktype_and_tape_serialize_to_official_strings() {
829        assert_eq!(TickType::Trade.as_str(), "trade");
830        assert_eq!(TickType::Quote.as_str(), "quote");
831        assert_eq!(Tape::A.as_str(), "A");
832        assert_eq!(Tape::B.as_str(), "B");
833        assert_eq!(Tape::C.as_str(), "C");
834    }
835
836    #[test]
837    fn metadata_request_serializes_official_query_words() {
838        let request = ConditionCodesRequest {
839            ticktype: TickType::Trade,
840            tape: Tape::A,
841        };
842
843        assert_eq!(
844            request.to_query(),
845            vec![("tape".to_string(), "A".to_string()),]
846        );
847    }
848}