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}