1#![allow(missing_docs)]
8
9use crate::client::AlpacaHttpClient;
10use alpaca_base::{OAuthToken, Result, types::*};
11use chrono::{DateTime, NaiveDate, Utc};
12use serde::{Deserialize, Serialize};
13use uuid::Uuid;
14
15impl AlpacaHttpClient {
16 pub async fn get_account(&self) -> Result<Account> {
20 self.get("/v2/account").await
21 }
22
23 pub async fn get_account_configurations(&self) -> Result<AccountConfigurations> {
25 self.get("/v2/account/configurations").await
26 }
27
28 pub async fn update_account_configurations(
30 &self,
31 config: &AccountConfigurations,
32 ) -> Result<AccountConfigurations> {
33 self.patch("/v2/account/configurations", config).await
34 }
35
36 pub async fn get_account_activities(
38 &self,
39 params: &ActivityParams,
40 ) -> Result<Vec<AccountActivity>> {
41 self.get_with_params("/v2/account/activities", params).await
42 }
43
44 pub async fn get_portfolio_history(
46 &self,
47 params: &PortfolioHistoryParams,
48 ) -> Result<PortfolioHistory> {
49 self.get_with_params("/v2/account/portfolio/history", params)
50 .await
51 }
52
53 pub async fn get_assets(&self, params: &AssetParams) -> Result<Vec<Asset>> {
57 self.get_with_params("/v2/assets", params).await
58 }
59
60 pub async fn get_asset(&self, asset_id: &Uuid) -> Result<Asset> {
62 self.get(&format!("/v2/assets/{}", asset_id)).await
63 }
64
65 pub async fn get_asset_by_symbol(&self, symbol: &str) -> Result<Asset> {
67 self.get(&format!("/v2/assets/{}", symbol)).await
68 }
69
70 pub async fn get_orders(&self, params: &OrderParams) -> Result<Vec<Order>> {
74 self.get_with_params("/v2/orders", params).await
75 }
76
77 pub async fn create_order(&self, order: &CreateOrderRequest) -> Result<Order> {
79 self.post("/v2/orders", order).await
80 }
81
82 pub async fn get_order(&self, order_id: &Uuid) -> Result<Order> {
84 self.get(&format!("/v2/orders/{}", order_id)).await
85 }
86
87 pub async fn get_order_by_client_id(&self, client_order_id: &str) -> Result<Order> {
89 self.get(&format!(
90 "/v2/orders:by_client_order_id?client_order_id={}",
91 client_order_id
92 ))
93 .await
94 }
95
96 pub async fn replace_order(
98 &self,
99 order_id: &Uuid,
100 order: &ReplaceOrderRequest,
101 ) -> Result<Order> {
102 self.patch(&format!("/v2/orders/{}", order_id), order).await
103 }
104
105 pub async fn cancel_order(&self, order_id: &Uuid) -> Result<()> {
107 self.delete(&format!("/v2/orders/{}", order_id)).await
108 }
109
110 pub async fn cancel_all_orders(&self) -> Result<Vec<CancelOrderResponse>> {
112 self.delete("/v2/orders").await
113 }
114
115 pub async fn get_positions(&self) -> Result<Vec<Position>> {
119 self.get("/v2/positions").await
120 }
121
122 pub async fn get_position(&self, symbol: &str) -> Result<Position> {
124 self.get(&format!("/v2/positions/{}", symbol)).await
125 }
126
127 pub async fn close_all_positions(
129 &self,
130 cancel_orders: bool,
131 ) -> Result<Vec<ClosePositionResponse>> {
132 let url = format!("/v2/positions?cancel_orders={}", cancel_orders);
133 self.delete(&url).await
134 }
135
136 pub async fn close_position(
138 &self,
139 symbol: &str,
140 _params: &ClosePositionRequest,
141 ) -> Result<Order> {
142 self.delete(&format!("/v2/positions/{}", symbol)).await
143 }
144
145 pub async fn get_watchlists(&self) -> Result<Vec<Watchlist>> {
149 self.get("/v2/watchlists").await
150 }
151
152 pub async fn create_watchlist(&self, watchlist: &CreateWatchlistRequest) -> Result<Watchlist> {
154 self.post("/v2/watchlists", watchlist).await
155 }
156
157 pub async fn get_watchlist(&self, watchlist_id: &Uuid) -> Result<Watchlist> {
159 self.get(&format!("/v2/watchlists/{}", watchlist_id)).await
160 }
161
162 pub async fn update_watchlist(
164 &self,
165 watchlist_id: &Uuid,
166 watchlist: &UpdateWatchlistRequest,
167 ) -> Result<Watchlist> {
168 self.put(&format!("/v2/watchlists/{}", watchlist_id), watchlist)
169 .await
170 }
171
172 pub async fn delete_watchlist(&self, watchlist_id: &Uuid) -> Result<()> {
174 self.delete(&format!("/v2/watchlists/{}", watchlist_id))
175 .await
176 }
177
178 pub async fn add_to_watchlist(&self, watchlist_id: &Uuid, symbol: &str) -> Result<Watchlist> {
180 let request = AddToWatchlistRequest {
181 symbol: symbol.to_string(),
182 };
183 self.post(&format!("/v2/watchlists/{}", watchlist_id), &request)
184 .await
185 }
186
187 pub async fn remove_from_watchlist(&self, watchlist_id: &Uuid, symbol: &str) -> Result<()> {
189 self.delete(&format!("/v2/watchlists/{}/{}", watchlist_id, symbol))
190 .await
191 }
192
193 pub async fn get_bars(&self, symbol: &str, params: &BarsParams) -> Result<BarsResponse> {
197 self.get_with_params(&format!("/v2/stocks/{}/bars", symbol), params)
198 .await
199 }
200
201 pub async fn get_quotes(&self, symbol: &str, params: &QuotesParams) -> Result<QuotesResponse> {
203 self.get_with_params(&format!("/v2/stocks/{}/quotes", symbol), params)
204 .await
205 }
206
207 pub async fn get_trades(&self, symbol: &str, params: &TradesParams) -> Result<TradesResponse> {
209 self.get_with_params(&format!("/v2/stocks/{}/trades", symbol), params)
210 .await
211 }
212
213 pub async fn get_latest_bar(&self, symbol: &str) -> Result<LatestBarResponse> {
215 self.get(&format!("/v2/stocks/{}/bars/latest", symbol))
216 .await
217 }
218
219 pub async fn get_latest_quote(&self, symbol: &str) -> Result<LatestQuoteResponse> {
221 self.get(&format!("/v2/stocks/{}/quotes/latest", symbol))
222 .await
223 }
224
225 pub async fn get_latest_trade(&self, symbol: &str) -> Result<LatestTradeResponse> {
227 self.get(&format!("/v2/stocks/{}/trades/latest", symbol))
228 .await
229 }
230
231 pub async fn get_calendar(&self, params: &CalendarParams) -> Result<Vec<Calendar>> {
235 self.get_with_params("/v2/calendar", params).await
236 }
237
238 pub async fn get_clock(&self) -> Result<Clock> {
240 self.get("/v2/clock").await
241 }
242
243 pub async fn get_news(&self, params: &NewsParams) -> Result<NewsResponse> {
247 self.get_with_params("/v1beta1/news", params).await
248 }
249
250 pub async fn get_crypto_bars(
254 &self,
255 symbol: &str,
256 params: &CryptoBarsParams,
257 ) -> Result<CryptoBarsResponse> {
258 self.get_with_params(&format!("/v1beta1/crypto/{}/bars", symbol), params)
259 .await
260 }
261
262 pub async fn get_crypto_quotes(
264 &self,
265 symbol: &str,
266 params: &CryptoQuotesParams,
267 ) -> Result<CryptoQuotesResponse> {
268 self.get_with_params(&format!("/v1beta1/crypto/{}/quotes", symbol), params)
269 .await
270 }
271
272 pub async fn get_crypto_trades(
274 &self,
275 symbol: &str,
276 params: &CryptoTradesParams,
277 ) -> Result<CryptoTradesResponse> {
278 self.get_with_params(&format!("/v1beta1/crypto/{}/trades", symbol), params)
279 .await
280 }
281}
282
283#[derive(Debug, Serialize, Deserialize)]
286pub struct AccountConfigurations {
287 pub dtbp_check: Option<String>,
288 pub trade_confirm_email: Option<String>,
289 pub suspend_trade: Option<bool>,
290 pub no_shorting: Option<bool>,
291 pub max_margin_multiplier: Option<String>,
292 pub pdt_check: Option<String>,
293 pub max_dte: Option<i32>,
294}
295
296#[derive(Debug, Serialize, Deserialize)]
297pub struct ActivityParams {
298 pub activity_type: Option<ActivityType>,
299 pub date: Option<String>,
300 pub until: Option<String>,
301 pub after: Option<String>,
302 pub direction: Option<String>,
303 pub page_size: Option<u32>,
304 pub page_token: Option<String>,
305}
306
307#[derive(Debug, Serialize, Deserialize)]
308pub struct PortfolioHistoryParams {
309 pub period: Option<String>,
310 pub timeframe: Option<String>,
311 pub date_end: Option<String>,
312 pub extended_hours: Option<bool>,
313}
314
315#[derive(Debug, Serialize, Deserialize, Default)]
316pub struct AssetParams {
317 pub status: Option<AssetStatus>,
318 pub asset_class: Option<AssetClass>,
319 pub exchange: Option<String>,
320 pub attributes: Option<String>,
321}
322
323#[derive(Debug, Default, Serialize, Deserialize)]
325pub struct OrderParams {
326 pub status: Option<OrderQueryStatus>,
328 pub limit: Option<u32>,
330 pub after: Option<DateTime<Utc>>,
332 pub until: Option<DateTime<Utc>>,
334 pub direction: Option<SortDirection>,
336 pub nested: Option<bool>,
338 pub symbols: Option<String>,
340 pub side: Option<OrderSide>,
342}
343
344impl OrderParams {
345 #[must_use]
347 pub fn new() -> Self {
348 Self::default()
349 }
350
351 #[must_use]
353 pub fn status(mut self, status: OrderQueryStatus) -> Self {
354 self.status = Some(status);
355 self
356 }
357
358 #[must_use]
360 pub fn limit(mut self, limit: u32) -> Self {
361 self.limit = Some(limit);
362 self
363 }
364
365 #[must_use]
367 pub fn after(mut self, after: DateTime<Utc>) -> Self {
368 self.after = Some(after);
369 self
370 }
371
372 #[must_use]
374 pub fn until(mut self, until: DateTime<Utc>) -> Self {
375 self.until = Some(until);
376 self
377 }
378
379 #[must_use]
381 pub fn direction(mut self, direction: SortDirection) -> Self {
382 self.direction = Some(direction);
383 self
384 }
385
386 #[must_use]
388 pub fn nested(mut self, nested: bool) -> Self {
389 self.nested = Some(nested);
390 self
391 }
392
393 #[must_use]
395 pub fn symbols(mut self, symbols: impl Into<String>) -> Self {
396 self.symbols = Some(symbols.into());
397 self
398 }
399
400 #[must_use]
402 pub fn side(mut self, side: OrderSide) -> Self {
403 self.side = Some(side);
404 self
405 }
406}
407
408#[derive(Debug, Default, Serialize, Deserialize)]
412pub struct CreateOrderRequest {
413 pub symbol: String,
415 pub qty: Option<String>,
417 pub notional: Option<String>,
419 pub side: OrderSide,
421 #[serde(rename = "type")]
423 pub order_type: OrderType,
424 pub time_in_force: TimeInForce,
426 pub limit_price: Option<String>,
428 pub stop_price: Option<String>,
430 pub trail_price: Option<String>,
432 pub trail_percent: Option<String>,
434 pub extended_hours: Option<bool>,
436 pub client_order_id: Option<String>,
438 pub order_class: Option<OrderClass>,
440 pub take_profit: Option<TakeProfit>,
442 pub stop_loss: Option<StopLoss>,
444 pub position_intent: Option<PositionIntent>,
446 #[serde(skip_serializing_if = "Option::is_none")]
448 pub gtd_date: Option<NaiveDate>,
449}
450
451impl CreateOrderRequest {
452 #[must_use]
454 pub fn market(symbol: impl Into<String>, side: OrderSide, qty: impl Into<String>) -> Self {
455 Self {
456 symbol: symbol.into(),
457 qty: Some(qty.into()),
458 side,
459 order_type: OrderType::Market,
460 time_in_force: TimeInForce::Day,
461 ..Default::default()
462 }
463 }
464
465 #[must_use]
467 pub fn limit(
468 symbol: impl Into<String>,
469 side: OrderSide,
470 qty: impl Into<String>,
471 limit_price: impl Into<String>,
472 ) -> Self {
473 Self {
474 symbol: symbol.into(),
475 qty: Some(qty.into()),
476 side,
477 order_type: OrderType::Limit,
478 time_in_force: TimeInForce::Day,
479 limit_price: Some(limit_price.into()),
480 ..Default::default()
481 }
482 }
483
484 #[must_use]
486 pub fn stop(
487 symbol: impl Into<String>,
488 side: OrderSide,
489 qty: impl Into<String>,
490 stop_price: impl Into<String>,
491 ) -> Self {
492 Self {
493 symbol: symbol.into(),
494 qty: Some(qty.into()),
495 side,
496 order_type: OrderType::Stop,
497 time_in_force: TimeInForce::Day,
498 stop_price: Some(stop_price.into()),
499 ..Default::default()
500 }
501 }
502
503 #[must_use]
505 pub fn stop_limit(
506 symbol: impl Into<String>,
507 side: OrderSide,
508 qty: impl Into<String>,
509 stop_price: impl Into<String>,
510 limit_price: impl Into<String>,
511 ) -> Self {
512 Self {
513 symbol: symbol.into(),
514 qty: Some(qty.into()),
515 side,
516 order_type: OrderType::StopLimit,
517 time_in_force: TimeInForce::Day,
518 stop_price: Some(stop_price.into()),
519 limit_price: Some(limit_price.into()),
520 ..Default::default()
521 }
522 }
523
524 #[must_use]
526 pub fn trailing_stop_price(
527 symbol: impl Into<String>,
528 side: OrderSide,
529 qty: impl Into<String>,
530 trail_price: impl Into<String>,
531 ) -> Self {
532 Self {
533 symbol: symbol.into(),
534 qty: Some(qty.into()),
535 side,
536 order_type: OrderType::TrailingStop,
537 time_in_force: TimeInForce::Day,
538 trail_price: Some(trail_price.into()),
539 ..Default::default()
540 }
541 }
542
543 #[must_use]
545 pub fn trailing_stop_percent(
546 symbol: impl Into<String>,
547 side: OrderSide,
548 qty: impl Into<String>,
549 trail_percent: impl Into<String>,
550 ) -> Self {
551 Self {
552 symbol: symbol.into(),
553 qty: Some(qty.into()),
554 side,
555 order_type: OrderType::TrailingStop,
556 time_in_force: TimeInForce::Day,
557 trail_percent: Some(trail_percent.into()),
558 ..Default::default()
559 }
560 }
561
562 #[must_use]
568 pub fn bracket(
569 symbol: impl Into<String>,
570 side: OrderSide,
571 qty: impl Into<String>,
572 order_type: OrderType,
573 take_profit: TakeProfit,
574 stop_loss: StopLoss,
575 ) -> Self {
576 Self {
577 symbol: symbol.into(),
578 qty: Some(qty.into()),
579 side,
580 order_type,
581 time_in_force: TimeInForce::Day,
582 order_class: Some(OrderClass::Bracket),
583 take_profit: Some(take_profit),
584 stop_loss: Some(stop_loss),
585 ..Default::default()
586 }
587 }
588
589 #[must_use]
594 pub fn oco(
595 symbol: impl Into<String>,
596 side: OrderSide,
597 qty: impl Into<String>,
598 take_profit: TakeProfit,
599 stop_loss: StopLoss,
600 ) -> Self {
601 Self {
602 symbol: symbol.into(),
603 qty: Some(qty.into()),
604 side,
605 order_type: OrderType::Limit,
606 time_in_force: TimeInForce::Day,
607 order_class: Some(OrderClass::Oco),
608 take_profit: Some(take_profit),
609 stop_loss: Some(stop_loss),
610 ..Default::default()
611 }
612 }
613
614 #[must_use]
619 pub fn oto(
620 symbol: impl Into<String>,
621 side: OrderSide,
622 qty: impl Into<String>,
623 order_type: OrderType,
624 stop_loss: StopLoss,
625 ) -> Self {
626 Self {
627 symbol: symbol.into(),
628 qty: Some(qty.into()),
629 side,
630 order_type,
631 time_in_force: TimeInForce::Day,
632 order_class: Some(OrderClass::Oto),
633 stop_loss: Some(stop_loss),
634 ..Default::default()
635 }
636 }
637
638 #[must_use]
640 pub fn time_in_force(mut self, tif: TimeInForce) -> Self {
641 self.time_in_force = tif;
642 self
643 }
644
645 #[must_use]
647 pub fn with_limit_price(mut self, price: impl Into<String>) -> Self {
648 self.limit_price = Some(price.into());
649 self
650 }
651
652 #[must_use]
654 pub fn extended_hours(mut self, enabled: bool) -> Self {
655 self.extended_hours = Some(enabled);
656 self
657 }
658
659 #[must_use]
661 pub fn client_order_id(mut self, id: impl Into<String>) -> Self {
662 self.client_order_id = Some(id.into());
663 self
664 }
665
666 #[must_use]
668 pub fn position_intent(mut self, intent: PositionIntent) -> Self {
669 self.position_intent = Some(intent);
670 self
671 }
672
673 #[must_use]
675 pub fn gtd_date(mut self, date: NaiveDate) -> Self {
676 self.time_in_force = TimeInForce::Gtd;
677 self.gtd_date = Some(date);
678 self
679 }
680}
681
682#[derive(Debug, Default, Serialize, Deserialize)]
684pub struct ReplaceOrderRequest {
685 pub qty: Option<String>,
687 pub time_in_force: Option<TimeInForce>,
689 pub limit_price: Option<String>,
691 pub stop_price: Option<String>,
693 pub trail: Option<String>,
695 pub client_order_id: Option<String>,
697}
698
699impl ReplaceOrderRequest {
700 #[must_use]
702 pub fn new() -> Self {
703 Self::default()
704 }
705
706 #[must_use]
708 pub fn qty(mut self, qty: impl Into<String>) -> Self {
709 self.qty = Some(qty.into());
710 self
711 }
712
713 #[must_use]
715 pub fn time_in_force(mut self, tif: TimeInForce) -> Self {
716 self.time_in_force = Some(tif);
717 self
718 }
719
720 #[must_use]
722 pub fn limit_price(mut self, price: impl Into<String>) -> Self {
723 self.limit_price = Some(price.into());
724 self
725 }
726
727 #[must_use]
729 pub fn stop_price(mut self, price: impl Into<String>) -> Self {
730 self.stop_price = Some(price.into());
731 self
732 }
733
734 #[must_use]
736 pub fn trail(mut self, trail: impl Into<String>) -> Self {
737 self.trail = Some(trail.into());
738 self
739 }
740
741 #[must_use]
743 pub fn client_order_id(mut self, id: impl Into<String>) -> Self {
744 self.client_order_id = Some(id.into());
745 self
746 }
747}
748
749#[derive(Debug, Serialize, Deserialize)]
750pub struct CancelOrderResponse {
751 pub id: Uuid,
752 pub status: i32,
753}
754
755#[derive(Debug, Serialize, Deserialize)]
756pub struct ClosePositionResponse {
757 pub symbol: String,
758 pub status: i32,
759}
760
761#[derive(Debug, Serialize, Deserialize, Default)]
763pub struct ClosePositionRequest {
764 pub qty: Option<String>,
766 pub percentage: Option<String>,
768}
769
770impl ClosePositionRequest {
771 #[must_use]
773 pub fn new() -> Self {
774 Self::default()
775 }
776
777 #[must_use]
779 pub fn qty(mut self, qty: impl Into<String>) -> Self {
780 self.qty = Some(qty.into());
781 self
782 }
783
784 #[must_use]
786 pub fn percentage(mut self, percentage: impl Into<String>) -> Self {
787 self.percentage = Some(percentage.into());
788 self
789 }
790}
791
792#[derive(Debug, Serialize, Deserialize)]
793pub struct CreateWatchlistRequest {
794 pub name: String,
795 pub symbols: Option<Vec<String>>,
796}
797
798#[derive(Debug, Serialize, Deserialize)]
799pub struct UpdateWatchlistRequest {
800 pub name: Option<String>,
801 pub symbols: Option<Vec<String>>,
802}
803
804#[derive(Debug, Serialize, Deserialize)]
805pub struct AddToWatchlistRequest {
806 pub symbol: String,
807}
808
809#[derive(Debug, Serialize, Deserialize, Default)]
810pub struct BarsParams {
811 pub start: Option<DateTime<Utc>>,
812 pub end: Option<DateTime<Utc>>,
813 pub timeframe: Option<String>,
814 pub page_token: Option<String>,
815 pub limit: Option<u32>,
816 pub asof: Option<String>,
817 pub feed: Option<String>,
818 pub sort: Option<String>,
819}
820
821#[derive(Debug, Serialize, Deserialize)]
822pub struct BarsResponse {
823 pub bars: Vec<Bar>,
824 pub symbol: String,
825 pub next_page_token: Option<String>,
826}
827
828#[derive(Debug, Serialize, Deserialize, Default)]
829pub struct QuotesParams {
830 pub start: Option<DateTime<Utc>>,
831 pub end: Option<DateTime<Utc>>,
832 pub page_token: Option<String>,
833 pub limit: Option<u32>,
834 pub asof: Option<String>,
835 pub feed: Option<String>,
836 pub sort: Option<String>,
837}
838
839#[derive(Debug, Serialize, Deserialize)]
840pub struct QuotesResponse {
841 pub quotes: Vec<Quote>,
842 pub symbol: String,
843 pub next_page_token: Option<String>,
844}
845
846#[derive(Debug, Serialize, Deserialize, Default)]
847pub struct TradesParams {
848 pub start: Option<DateTime<Utc>>,
849 pub end: Option<DateTime<Utc>>,
850 pub page_token: Option<String>,
851 pub limit: Option<u32>,
852 pub asof: Option<String>,
853 pub feed: Option<String>,
854 pub sort: Option<String>,
855}
856
857#[derive(Debug, Serialize, Deserialize)]
858pub struct TradesResponse {
859 pub trades: Vec<Trade>,
860 pub symbol: String,
861 pub next_page_token: Option<String>,
862}
863
864#[derive(Debug, Serialize, Deserialize)]
865pub struct LatestBarResponse {
866 pub bar: Bar,
867 pub symbol: String,
868}
869
870#[derive(Debug, Serialize, Deserialize)]
871pub struct LatestQuoteResponse {
872 pub quote: Quote,
873 pub symbol: String,
874}
875
876#[derive(Debug, Serialize, Deserialize)]
877pub struct LatestTradeResponse {
878 pub trade: Trade,
879 pub symbol: String,
880}
881
882#[derive(Debug, Serialize, Deserialize, Default)]
883pub struct CalendarParams {
884 pub start: Option<String>,
885 pub end: Option<String>,
886}
887
888impl CalendarParams {
889 #[must_use]
891 pub fn new() -> Self {
892 Self::default()
893 }
894
895 #[must_use]
897 pub fn start(mut self, start: &str) -> Self {
898 self.start = Some(start.to_string());
899 self
900 }
901
902 #[must_use]
904 pub fn end(mut self, end: &str) -> Self {
905 self.end = Some(end.to_string());
906 self
907 }
908}
909
910#[derive(Debug, Serialize, Deserialize)]
911pub struct NewsParams {
912 pub symbols: Option<String>,
913 pub start: Option<DateTime<Utc>>,
914 pub end: Option<DateTime<Utc>>,
915 pub sort: Option<String>,
916 pub include_content: Option<bool>,
917 pub exclude_contentless: Option<bool>,
918 pub page_token: Option<String>,
919 pub limit: Option<u32>,
920}
921
922#[derive(Debug, Serialize, Deserialize)]
923pub struct NewsResponse {
924 pub news: Vec<NewsArticle>,
925 pub next_page_token: Option<String>,
926}
927
928#[derive(Debug, Serialize, Deserialize)]
929pub struct CryptoBarsParams {
930 pub start: Option<DateTime<Utc>>,
931 pub end: Option<DateTime<Utc>>,
932 pub timeframe: Option<String>,
933 pub page_token: Option<String>,
934 pub limit: Option<u32>,
935 pub sort: Option<String>,
936}
937
938#[derive(Debug, Serialize, Deserialize)]
939pub struct CryptoBarsResponse {
940 pub bars: Vec<Bar>,
941 pub symbol: String,
942 pub next_page_token: Option<String>,
943}
944
945#[derive(Debug, Serialize, Deserialize)]
946pub struct CryptoQuotesParams {
947 pub start: Option<DateTime<Utc>>,
948 pub end: Option<DateTime<Utc>>,
949 pub page_token: Option<String>,
950 pub limit: Option<u32>,
951 pub sort: Option<String>,
952}
953
954#[derive(Debug, Serialize, Deserialize)]
955pub struct CryptoQuotesResponse {
956 pub quotes: Vec<Quote>,
957 pub symbol: String,
958 pub next_page_token: Option<String>,
959}
960
961#[derive(Debug, Serialize, Deserialize)]
962pub struct CryptoTradesParams {
963 pub start: Option<DateTime<Utc>>,
964 pub end: Option<DateTime<Utc>>,
965 pub page_token: Option<String>,
966 pub limit: Option<u32>,
967 pub sort: Option<String>,
968}
969
970#[derive(Debug, Serialize, Deserialize)]
971pub struct CryptoTradesResponse {
972 pub trades: Vec<Trade>,
973 pub symbol: String,
974 pub next_page_token: Option<String>,
975}
976
977#[derive(Debug, Serialize, Deserialize)]
983pub struct OptionContractsResponse {
984 pub option_contracts: Vec<OptionContract>,
986 pub next_page_token: Option<String>,
988}
989
990#[derive(Debug, Serialize, Deserialize)]
992pub struct OptionBarsResponse {
993 pub bars: std::collections::HashMap<String, Vec<OptionBar>>,
995 pub next_page_token: Option<String>,
997}
998
999#[derive(Debug, Serialize, Deserialize)]
1001pub struct OptionSnapshotsResponse {
1002 pub snapshots: std::collections::HashMap<String, OptionSnapshot>,
1004}
1005
1006impl AlpacaHttpClient {
1007 pub async fn get_option_contracts(
1019 &self,
1020 params: &OptionContractParams,
1021 ) -> Result<OptionContractsResponse> {
1022 self.get_with_params("/v2/options/contracts", params).await
1023 }
1024
1025 pub async fn get_option_contract(&self, symbol_or_id: &str) -> Result<OptionContract> {
1033 self.get(&format!("/v2/options/contracts/{}", symbol_or_id))
1034 .await
1035 }
1036
1037 pub async fn exercise_option(&self, request: &OptionExerciseRequest) -> Result<Order> {
1045 self.post("/v2/options/exercise", request).await
1046 }
1047
1048 pub async fn get_option_bars(&self, params: &OptionBarsParams) -> Result<OptionBarsResponse> {
1060 self.get_with_params("/v1beta1/options/bars", params).await
1061 }
1062
1063 pub async fn get_option_snapshots(&self, symbols: &str) -> Result<OptionSnapshotsResponse> {
1071 #[derive(Serialize)]
1072 struct Params<'a> {
1073 symbols: &'a str,
1074 }
1075 self.get_with_params("/v1beta1/options/snapshots", &Params { symbols })
1076 .await
1077 }
1078
1079 pub async fn get_option_chain(
1087 &self,
1088 underlying_symbol: &str,
1089 ) -> Result<OptionSnapshotsResponse> {
1090 #[derive(Serialize)]
1091 struct Params<'a> {
1092 underlying_symbol: &'a str,
1093 }
1094 self.get_with_params("/v1beta1/options/snapshots", &Params { underlying_symbol })
1095 .await
1096 }
1097}
1098
1099#[derive(Debug, Serialize, Deserialize)]
1105pub struct MultiBarsResponse {
1106 pub bars: std::collections::HashMap<String, Vec<Bar>>,
1108 pub next_page_token: Option<String>,
1110}
1111
1112#[derive(Debug, Serialize, Deserialize)]
1114pub struct MultiQuotesResponse {
1115 pub quotes: std::collections::HashMap<String, Vec<Quote>>,
1117 pub next_page_token: Option<String>,
1119}
1120
1121#[derive(Debug, Serialize, Deserialize)]
1123pub struct MultiTradesResponse {
1124 pub trades: std::collections::HashMap<String, Vec<Trade>>,
1126 pub next_page_token: Option<String>,
1128}
1129
1130#[derive(Debug, Serialize, Deserialize)]
1132pub struct StockSnapshotsResponse {
1133 #[serde(flatten)]
1135 pub snapshots: std::collections::HashMap<String, StockSnapshot>,
1136}
1137
1138#[derive(Debug, Serialize, Deserialize)]
1140pub struct CorporateActionsResponse {
1141 pub corporate_actions: Vec<CorporateAction>,
1143 pub next_page_token: Option<String>,
1145}
1146
1147#[derive(Debug, Serialize, Deserialize)]
1149pub struct LatestBarsResponse {
1150 pub bars: std::collections::HashMap<String, Bar>,
1152}
1153
1154#[derive(Debug, Serialize, Deserialize)]
1156pub struct LatestQuotesResponse {
1157 pub quotes: std::collections::HashMap<String, Quote>,
1159}
1160
1161#[derive(Debug, Serialize, Deserialize)]
1163pub struct LatestTradesResponse {
1164 pub trades: std::collections::HashMap<String, Trade>,
1166}
1167
1168impl AlpacaHttpClient {
1169 pub async fn get_stock_bars(&self, params: &MultiBarsParams) -> Result<MultiBarsResponse> {
1181 self.get_with_params("/v2/stocks/bars", params).await
1182 }
1183
1184 pub async fn get_stock_quotes(
1192 &self,
1193 params: &MultiQuotesParams,
1194 ) -> Result<MultiQuotesResponse> {
1195 self.get_with_params("/v2/stocks/quotes", params).await
1196 }
1197
1198 pub async fn get_stock_trades(
1206 &self,
1207 params: &MultiTradesParams,
1208 ) -> Result<MultiTradesResponse> {
1209 self.get_with_params("/v2/stocks/trades", params).await
1210 }
1211
1212 pub async fn get_stock_snapshots(&self, symbols: &str) -> Result<StockSnapshotsResponse> {
1220 #[derive(Serialize)]
1221 struct Params<'a> {
1222 symbols: &'a str,
1223 }
1224 self.get_with_params("/v2/stocks/snapshots", &Params { symbols })
1225 .await
1226 }
1227
1228 pub async fn get_latest_bars(&self, symbols: &str) -> Result<LatestBarsResponse> {
1240 #[derive(Serialize)]
1241 struct Params<'a> {
1242 symbols: &'a str,
1243 }
1244 self.get_with_params("/v2/stocks/bars/latest", &Params { symbols })
1245 .await
1246 }
1247
1248 pub async fn get_latest_quotes(&self, symbols: &str) -> Result<LatestQuotesResponse> {
1256 #[derive(Serialize)]
1257 struct Params<'a> {
1258 symbols: &'a str,
1259 }
1260 self.get_with_params("/v2/stocks/quotes/latest", &Params { symbols })
1261 .await
1262 }
1263
1264 pub async fn get_latest_trades(&self, symbols: &str) -> Result<LatestTradesResponse> {
1272 #[derive(Serialize)]
1273 struct Params<'a> {
1274 symbols: &'a str,
1275 }
1276 self.get_with_params("/v2/stocks/trades/latest", &Params { symbols })
1277 .await
1278 }
1279
1280 pub async fn get_corporate_actions(
1292 &self,
1293 params: &CorporateActionsParams,
1294 ) -> Result<CorporateActionsResponse> {
1295 self.get_with_params("/v1beta1/corporate-actions/announcements", params)
1296 .await
1297 }
1298}
1299
1300impl AlpacaHttpClient {
1305 pub async fn create_broker_account(
1317 &self,
1318 request: &CreateBrokerAccountRequest,
1319 ) -> Result<BrokerAccount> {
1320 self.post("/v1/accounts", request).await
1321 }
1322
1323 pub async fn list_broker_accounts(
1331 &self,
1332 params: &ListBrokerAccountsParams,
1333 ) -> Result<Vec<BrokerAccount>> {
1334 self.get_with_params("/v1/accounts", params).await
1335 }
1336
1337 pub async fn get_broker_account(&self, account_id: &str) -> Result<BrokerAccount> {
1345 self.get(&format!("/v1/accounts/{}", account_id)).await
1346 }
1347
1348 pub async fn update_broker_account(
1357 &self,
1358 account_id: &str,
1359 request: &UpdateBrokerAccountRequest,
1360 ) -> Result<BrokerAccount> {
1361 self.patch(&format!("/v1/accounts/{}", account_id), request)
1362 .await
1363 }
1364
1365 pub async fn close_broker_account(&self, account_id: &str) -> Result<()> {
1370 self.delete(&format!("/v1/accounts/{}", account_id)).await
1371 }
1372
1373 pub async fn get_broker_trading_account(&self, account_id: &str) -> Result<Account> {
1381 self.get(&format!("/v1/accounts/{}/trading", account_id))
1382 .await
1383 }
1384
1385 pub async fn submit_cip(&self, account_id: &str, cip_info: &CipInfo) -> Result<CipInfo> {
1398 self.post(&format!("/v1/accounts/{}/cip", account_id), cip_info)
1399 .await
1400 }
1401
1402 pub async fn get_cip(&self, account_id: &str) -> Result<CipInfo> {
1410 self.get(&format!("/v1/accounts/{}/cip", account_id)).await
1411 }
1412
1413 pub async fn upload_document(
1426 &self,
1427 account_id: &str,
1428 document: &Document,
1429 ) -> Result<DocumentUploadResponse> {
1430 self.post(
1431 &format!("/v1/accounts/{}/documents/upload", account_id),
1432 document,
1433 )
1434 .await
1435 }
1436
1437 pub async fn list_documents(&self, account_id: &str) -> Result<Vec<DocumentInfo>> {
1445 self.get(&format!("/v1/accounts/{}/documents", account_id))
1446 .await
1447 }
1448
1449 pub async fn get_document(&self, account_id: &str, document_id: &str) -> Result<DocumentInfo> {
1458 self.get(&format!(
1459 "/v1/accounts/{}/documents/{}",
1460 account_id, document_id
1461 ))
1462 .await
1463 }
1464
1465 pub async fn delete_document(&self, account_id: &str, document_id: &str) -> Result<()> {
1471 self.delete(&format!(
1472 "/v1/accounts/{}/documents/{}",
1473 account_id, document_id
1474 ))
1475 .await
1476 }
1477}
1478
1479#[derive(Debug, Serialize, Deserialize)]
1481pub struct DocumentUploadResponse {
1482 pub id: String,
1484 pub document_type: DocumentType,
1486 pub status: String,
1488}
1489
1490#[derive(Debug, Serialize, Deserialize)]
1492pub struct DocumentInfo {
1493 pub id: String,
1495 pub document_type: DocumentType,
1497 #[serde(skip_serializing_if = "Option::is_none")]
1499 pub document_sub_type: Option<String>,
1500 pub created_at: DateTime<Utc>,
1502}
1503
1504impl AlpacaHttpClient {
1509 pub async fn create_ach_relationship(
1522 &self,
1523 account_id: &str,
1524 request: &CreateAchRelationshipRequest,
1525 ) -> Result<AchRelationship> {
1526 self.post(
1527 &format!("/v1/accounts/{}/ach_relationships", account_id),
1528 request,
1529 )
1530 .await
1531 }
1532
1533 pub async fn list_ach_relationships(&self, account_id: &str) -> Result<Vec<AchRelationship>> {
1541 self.get(&format!("/v1/accounts/{}/ach_relationships", account_id))
1542 .await
1543 }
1544
1545 pub async fn delete_ach_relationship(
1551 &self,
1552 account_id: &str,
1553 relationship_id: &str,
1554 ) -> Result<()> {
1555 self.delete(&format!(
1556 "/v1/accounts/{}/ach_relationships/{}",
1557 account_id, relationship_id
1558 ))
1559 .await
1560 }
1561
1562 pub async fn create_transfer(
1575 &self,
1576 account_id: &str,
1577 request: &CreateTransferRequest,
1578 ) -> Result<Transfer> {
1579 self.post(&format!("/v1/accounts/{}/transfers", account_id), request)
1580 .await
1581 }
1582
1583 pub async fn list_transfers(
1592 &self,
1593 account_id: &str,
1594 params: &ListTransfersParams,
1595 ) -> Result<Vec<Transfer>> {
1596 self.get_with_params(&format!("/v1/accounts/{}/transfers", account_id), params)
1597 .await
1598 }
1599
1600 pub async fn get_transfer(&self, account_id: &str, transfer_id: &str) -> Result<Transfer> {
1609 self.get(&format!(
1610 "/v1/accounts/{}/transfers/{}",
1611 account_id, transfer_id
1612 ))
1613 .await
1614 }
1615
1616 pub async fn cancel_transfer(&self, account_id: &str, transfer_id: &str) -> Result<()> {
1622 self.delete(&format!(
1623 "/v1/accounts/{}/transfers/{}",
1624 account_id, transfer_id
1625 ))
1626 .await
1627 }
1628
1629 pub async fn list_wire_banks(&self, account_id: &str) -> Result<Vec<WireBank>> {
1641 self.get(&format!("/v1/accounts/{}/recipient_banks", account_id))
1642 .await
1643 }
1644
1645 pub async fn create_wire_bank(
1654 &self,
1655 account_id: &str,
1656 request: &CreateWireBankRequest,
1657 ) -> Result<WireBank> {
1658 self.post(
1659 &format!("/v1/accounts/{}/recipient_banks", account_id),
1660 request,
1661 )
1662 .await
1663 }
1664
1665 pub async fn delete_wire_bank(&self, account_id: &str, bank_id: &str) -> Result<()> {
1671 self.delete(&format!(
1672 "/v1/accounts/{}/recipient_banks/{}",
1673 account_id, bank_id
1674 ))
1675 .await
1676 }
1677
1678 pub async fn create_journal(&self, request: &CreateJournalRequest) -> Result<Journal> {
1690 self.post("/v1/journals", request).await
1691 }
1692
1693 pub async fn list_journals(&self, params: &ListJournalsParams) -> Result<Vec<Journal>> {
1701 self.get_with_params("/v1/journals", params).await
1702 }
1703
1704 pub async fn create_batch_journals(
1712 &self,
1713 request: &CreateBatchJournalRequest,
1714 ) -> Result<Vec<Journal>> {
1715 self.post("/v1/journals/batch", request).await
1716 }
1717
1718 pub async fn delete_journal(&self, journal_id: &str) -> Result<()> {
1723 self.delete(&format!("/v1/journals/{}", journal_id)).await
1724 }
1725}
1726
1727#[derive(Debug, Serialize, Deserialize)]
1733pub struct MultiCryptoSnapshotsResponse {
1734 pub snapshots: std::collections::HashMap<String, CryptoSnapshot>,
1736}
1737
1738#[derive(Debug, Serialize, Deserialize)]
1740pub struct MultiCryptoBarsResponse {
1741 pub bars: std::collections::HashMap<String, Vec<CryptoBar>>,
1743 pub next_page_token: Option<String>,
1745}
1746
1747#[derive(Debug, Serialize, Deserialize)]
1749pub struct LatestCryptoBarsResponse {
1750 pub bars: std::collections::HashMap<String, CryptoBar>,
1752}
1753
1754#[derive(Debug, Serialize, Deserialize)]
1756pub struct LatestCryptoQuotesResponse {
1757 pub quotes: std::collections::HashMap<String, CryptoQuote>,
1759}
1760
1761#[derive(Debug, Serialize, Deserialize)]
1763pub struct LatestCryptoTradesResponse {
1764 pub trades: std::collections::HashMap<String, CryptoTrade>,
1766}
1767
1768#[derive(Debug, Serialize, Deserialize)]
1770pub struct CryptoOrderbooksResponse {
1771 pub orderbooks: std::collections::HashMap<String, CryptoOrderbook>,
1773}
1774
1775impl AlpacaHttpClient {
1776 pub async fn list_crypto_wallets(&self, account_id: &str) -> Result<Vec<BrokerCryptoWallet>> {
1788 self.get(&format!("/v1/accounts/{}/wallets", account_id))
1789 .await
1790 }
1791
1792 pub async fn create_crypto_wallet(
1801 &self,
1802 account_id: &str,
1803 request: &CreateCryptoWalletRequest,
1804 ) -> Result<BrokerCryptoWallet> {
1805 self.post(&format!("/v1/accounts/{}/wallets", account_id), request)
1806 .await
1807 }
1808
1809 pub async fn get_crypto_wallet(
1818 &self,
1819 account_id: &str,
1820 asset: &str,
1821 ) -> Result<BrokerCryptoWallet> {
1822 self.get(&format!("/v1/accounts/{}/wallets/{}", account_id, asset))
1823 .await
1824 }
1825
1826 pub async fn list_crypto_transfers(&self, account_id: &str) -> Result<Vec<CryptoTransfer>> {
1834 self.get(&format!("/v1/accounts/{}/wallets/transfers", account_id))
1835 .await
1836 }
1837
1838 pub async fn create_crypto_transfer(
1848 &self,
1849 account_id: &str,
1850 asset: &str,
1851 request: &CreateCryptoTransferRequest,
1852 ) -> Result<CryptoTransfer> {
1853 self.post(
1854 &format!("/v1/accounts/{}/wallets/{}/transfers", account_id, asset),
1855 request,
1856 )
1857 .await
1858 }
1859
1860 pub async fn list_crypto_whitelists(
1868 &self,
1869 account_id: &str,
1870 ) -> Result<Vec<CryptoWhitelistAddress>> {
1871 self.get(&format!("/v1/accounts/{}/wallets/whitelists", account_id))
1872 .await
1873 }
1874
1875 pub async fn create_crypto_whitelist(
1884 &self,
1885 account_id: &str,
1886 request: &CreateCryptoWhitelistRequest,
1887 ) -> Result<CryptoWhitelistAddress> {
1888 self.post(
1889 &format!("/v1/accounts/{}/wallets/whitelists", account_id),
1890 request,
1891 )
1892 .await
1893 }
1894
1895 pub async fn get_multi_crypto_bars(
1907 &self,
1908 params: &CryptoBarsParams,
1909 ) -> Result<MultiCryptoBarsResponse> {
1910 self.get_with_params("/v1beta3/crypto/us/bars", params)
1911 .await
1912 }
1913
1914 pub async fn get_latest_crypto_bars(&self, symbols: &str) -> Result<LatestCryptoBarsResponse> {
1922 #[derive(Serialize)]
1923 struct Params<'a> {
1924 symbols: &'a str,
1925 }
1926 self.get_with_params("/v1beta3/crypto/us/latest/bars", &Params { symbols })
1927 .await
1928 }
1929
1930 pub async fn get_latest_crypto_quotes(
1938 &self,
1939 symbols: &str,
1940 ) -> Result<LatestCryptoQuotesResponse> {
1941 #[derive(Serialize)]
1942 struct Params<'a> {
1943 symbols: &'a str,
1944 }
1945 self.get_with_params("/v1beta3/crypto/us/latest/quotes", &Params { symbols })
1946 .await
1947 }
1948
1949 pub async fn get_latest_crypto_trades(
1957 &self,
1958 symbols: &str,
1959 ) -> Result<LatestCryptoTradesResponse> {
1960 #[derive(Serialize)]
1961 struct Params<'a> {
1962 symbols: &'a str,
1963 }
1964 self.get_with_params("/v1beta3/crypto/us/latest/trades", &Params { symbols })
1965 .await
1966 }
1967
1968 pub async fn get_multi_crypto_snapshots(
1976 &self,
1977 symbols: &str,
1978 ) -> Result<MultiCryptoSnapshotsResponse> {
1979 #[derive(Serialize)]
1980 struct Params<'a> {
1981 symbols: &'a str,
1982 }
1983 self.get_with_params("/v1beta3/crypto/us/snapshots", &Params { symbols })
1984 .await
1985 }
1986
1987 pub async fn get_crypto_orderbooks(&self, symbols: &str) -> Result<CryptoOrderbooksResponse> {
1995 #[derive(Serialize)]
1996 struct Params<'a> {
1997 symbols: &'a str,
1998 }
1999 self.get_with_params("/v1beta3/crypto/us/latest/orderbooks", &Params { symbols })
2000 .await
2001 }
2002}
2003
2004#[derive(Debug, Serialize, Deserialize)]
2010pub struct EnhancedNewsResponse {
2011 pub news: Vec<EnhancedNewsArticle>,
2013 pub next_page_token: Option<String>,
2015}
2016
2017impl AlpacaHttpClient {
2018 pub async fn get_enhanced_news(
2026 &self,
2027 params: &alpaca_base::NewsParams,
2028 ) -> Result<EnhancedNewsResponse> {
2029 self.get_with_params("/v1beta1/news", params).await
2030 }
2031
2032 pub async fn get_enhanced_news_for_symbols(
2041 &self,
2042 symbols: &str,
2043 limit: u32,
2044 ) -> Result<EnhancedNewsResponse> {
2045 let params = alpaca_base::NewsParams::new().symbols(symbols).limit(limit);
2046 self.get_enhanced_news(¶ms).await
2047 }
2048
2049 pub async fn get_latest_enhanced_news(&self, limit: u32) -> Result<EnhancedNewsResponse> {
2057 let params = alpaca_base::NewsParams::new().sort_desc().limit(limit);
2058 self.get_enhanced_news(¶ms).await
2059 }
2060}
2061
2062impl AlpacaHttpClient {
2067 pub async fn oauth_exchange_code(&self, request: &OAuthTokenRequest) -> Result<OAuthToken> {
2075 self.post("/oauth/token", request).await
2076 }
2077
2078 pub async fn oauth_refresh_token(&self, request: &OAuthTokenRequest) -> Result<OAuthToken> {
2086 self.post("/oauth/token", request).await
2087 }
2088
2089 pub async fn oauth_revoke_token(&self, request: &OAuthRevokeRequest) -> Result<()> {
2094 let _: serde_json::Value = self.post("/oauth/revoke", request).await?;
2095 Ok(())
2096 }
2097}
2098
2099impl AlpacaHttpClient {
2104 #[must_use]
2114 pub fn get_account_status_events_url(&self, params: &SseEventParams) -> String {
2115 let base = "/v1/events/accounts/status";
2116 self.build_sse_url(base, params)
2117 }
2118
2119 #[must_use]
2129 pub fn get_transfer_status_events_url(&self, params: &SseEventParams) -> String {
2130 let base = "/v1/events/transfers/status";
2131 self.build_sse_url(base, params)
2132 }
2133
2134 #[must_use]
2144 pub fn get_trade_events_url(&self, params: &SseEventParams) -> String {
2145 let base = "/v1/events/trades";
2146 self.build_sse_url(base, params)
2147 }
2148
2149 #[must_use]
2159 pub fn get_journal_status_events_url(&self, params: &SseEventParams) -> String {
2160 let base = "/v1/events/journals/status";
2161 self.build_sse_url(base, params)
2162 }
2163
2164 #[must_use]
2174 pub fn get_nta_events_url(&self, params: &SseEventParams) -> String {
2175 let base = "/v2beta1/events/nta";
2176 self.build_sse_url(base, params)
2177 }
2178
2179 fn build_sse_url(&self, base: &str, params: &SseEventParams) -> String {
2181 let mut url = base.to_string();
2182 let mut query_parts = Vec::new();
2183
2184 if let Some(ref account_id) = params.account_id {
2185 query_parts.push(format!("account_id={}", account_id));
2186 }
2187 if let Some(ref since) = params.since {
2188 query_parts.push(format!("since={}", since));
2189 }
2190 if let Some(ref until) = params.until {
2191 query_parts.push(format!("until={}", until));
2192 }
2193
2194 if !query_parts.is_empty() {
2195 url.push('?');
2196 url.push_str(&query_parts.join("&"));
2197 }
2198
2199 url
2200 }
2201}
2202
2203impl AlpacaHttpClient {
2208 pub async fn list_enhanced_assets(
2216 &self,
2217 params: &ListAssetsParams,
2218 ) -> Result<Vec<EnhancedAsset>> {
2219 self.get_with_params("/v2/assets", params).await
2220 }
2221
2222 pub async fn get_enhanced_asset(&self, symbol_or_id: &str) -> Result<EnhancedAsset> {
2230 self.get(&format!("/v2/assets/{}", symbol_or_id)).await
2231 }
2232
2233 pub async fn get_asset_options(&self, symbol: &str) -> Result<Vec<OptionContractAsset>> {
2241 self.get(&format!("/v2/assets/{}/options", symbol)).await
2242 }
2243
2244 pub async fn list_announcements(
2252 &self,
2253 params: &ListAnnouncementsParams,
2254 ) -> Result<Vec<CorporateActionAnnouncement>> {
2255 self.get_with_params("/v1beta1/corporate-actions/announcements", params)
2256 .await
2257 }
2258
2259 pub async fn get_announcement(
2267 &self,
2268 announcement_id: &str,
2269 ) -> Result<CorporateActionAnnouncement> {
2270 self.get(&format!(
2271 "/v1beta1/corporate-actions/announcements/{}",
2272 announcement_id
2273 ))
2274 .await
2275 }
2276}
2277
2278impl AlpacaHttpClient {
2283 pub async fn list_activities(
2291 &self,
2292 params: &ListActivitiesParams,
2293 ) -> Result<Vec<AccountActivity>> {
2294 self.get_with_params("/v2/account/activities", params).await
2295 }
2296
2297 pub async fn list_activities_by_type(
2306 &self,
2307 activity_type: &str,
2308 params: &ListActivitiesParams,
2309 ) -> Result<Vec<AccountActivity>> {
2310 self.get_with_params(&format!("/v2/account/activities/{}", activity_type), params)
2311 .await
2312 }
2313
2314 pub async fn list_broker_activities(
2322 &self,
2323 params: &ListActivitiesParams,
2324 ) -> Result<Vec<AccountActivity>> {
2325 self.get_with_params("/v1/accounts/activities", params)
2326 .await
2327 }
2328
2329 pub async fn list_broker_account_activities(
2338 &self,
2339 account_id: &str,
2340 params: &ListActivitiesParams,
2341 ) -> Result<Vec<AccountActivity>> {
2342 self.get_with_params(&format!("/v1/accounts/{}/activities", account_id), params)
2343 .await
2344 }
2345}
2346
2347impl AlpacaHttpClient {
2352 pub async fn create_rebalance_portfolio(
2360 &self,
2361 request: &RebalancePortfolioRequest,
2362 ) -> Result<RebalancePortfolio> {
2363 self.post("/v1/rebalancing/portfolios", request).await
2364 }
2365
2366 pub async fn list_rebalance_portfolios(&self) -> Result<Vec<RebalancePortfolio>> {
2371 self.get("/v1/rebalancing/portfolios").await
2372 }
2373
2374 pub async fn get_rebalance_portfolio(&self, portfolio_id: &str) -> Result<RebalancePortfolio> {
2382 self.get(&format!("/v1/rebalancing/portfolios/{}", portfolio_id))
2383 .await
2384 }
2385
2386 pub async fn delete_rebalance_portfolio(&self, portfolio_id: &str) -> Result<()> {
2391 let _: serde_json::Value = self
2392 .delete(&format!("/v1/rebalancing/portfolios/{}", portfolio_id))
2393 .await?;
2394 Ok(())
2395 }
2396
2397 pub async fn execute_rebalance(&self, request: &RebalanceRunRequest) -> Result<RebalanceRun> {
2405 self.post("/v1/rebalancing/runs", request).await
2406 }
2407
2408 pub async fn list_rebalance_runs(&self) -> Result<Vec<RebalanceRun>> {
2413 self.get("/v1/rebalancing/runs").await
2414 }
2415}
2416
2417impl AlpacaHttpClient {
2422 pub async fn get_locates(&self) -> Result<Vec<LocateResponse>> {
2427 self.get("/v1/locate/stocks").await
2428 }
2429
2430 pub async fn request_locate(&self, request: &LocateRequest) -> Result<LocateResponse> {
2438 self.post("/v1/locate/stocks", request).await
2439 }
2440}
2441
2442impl AlpacaHttpClient {
2447 pub async fn reset_paper_account(&self) -> Result<Account> {
2452 self.post("/v2/account/reset", &serde_json::json!({})).await
2453 }
2454}
2455
2456impl AlpacaHttpClient {
2461 pub async fn get_exchange_rates(&self) -> Result<Vec<ExchangeRate>> {
2466 self.get("/v1/fx/rates").await
2467 }
2468
2469 pub async fn get_exchange_rate(&self, currency_pair: &str) -> Result<ExchangeRate> {
2477 self.get(&format!("/v1/fx/rates/{}", currency_pair)).await
2478 }
2479}
2480
2481impl AlpacaHttpClient {
2486 pub async fn list_ira_contributions(&self, account_id: &str) -> Result<Vec<IraContribution>> {
2494 self.get(&format!("/v1/accounts/{}/ira/contributions", account_id))
2495 .await
2496 }
2497
2498 pub async fn create_ira_contribution(
2507 &self,
2508 account_id: &str,
2509 request: &CreateIraContributionRequest,
2510 ) -> Result<IraContribution> {
2511 self.post(
2512 &format!("/v1/accounts/{}/ira/contributions", account_id),
2513 request,
2514 )
2515 .await
2516 }
2517
2518 pub async fn list_ira_distributions(&self, account_id: &str) -> Result<Vec<IraDistribution>> {
2526 self.get(&format!("/v1/accounts/{}/ira/distributions", account_id))
2527 .await
2528 }
2529
2530 pub async fn list_ira_beneficiaries(&self, account_id: &str) -> Result<Vec<IraBeneficiary>> {
2538 self.get(&format!("/v1/accounts/{}/ira/beneficiaries", account_id))
2539 .await
2540 }
2541}
2542
2543#[cfg(test)]
2544mod tests {
2545 use super::*;
2546
2547 #[test]
2548 fn test_create_order_request_market() {
2549 let order = CreateOrderRequest::market("AAPL", OrderSide::Buy, "10");
2550 assert_eq!(order.symbol, "AAPL");
2551 assert_eq!(order.side, OrderSide::Buy);
2552 assert_eq!(order.qty, Some("10".to_string()));
2553 assert_eq!(order.order_type, OrderType::Market);
2554 assert_eq!(order.time_in_force, TimeInForce::Day);
2555 }
2556
2557 #[test]
2558 fn test_create_order_request_limit() {
2559 let order = CreateOrderRequest::limit("AAPL", OrderSide::Buy, "10", "150.00");
2560 assert_eq!(order.symbol, "AAPL");
2561 assert_eq!(order.limit_price, Some("150.00".to_string()));
2562 assert_eq!(order.order_type, OrderType::Limit);
2563 }
2564
2565 #[test]
2566 fn test_create_order_request_stop() {
2567 let order = CreateOrderRequest::stop("AAPL", OrderSide::Sell, "10", "145.00");
2568 assert_eq!(order.stop_price, Some("145.00".to_string()));
2569 assert_eq!(order.order_type, OrderType::Stop);
2570 }
2571
2572 #[test]
2573 fn test_create_order_request_stop_limit() {
2574 let order =
2575 CreateOrderRequest::stop_limit("AAPL", OrderSide::Sell, "10", "145.00", "144.50");
2576 assert_eq!(order.stop_price, Some("145.00".to_string()));
2577 assert_eq!(order.limit_price, Some("144.50".to_string()));
2578 assert_eq!(order.order_type, OrderType::StopLimit);
2579 }
2580
2581 #[test]
2582 fn test_create_order_request_trailing_stop_price() {
2583 let order = CreateOrderRequest::trailing_stop_price("AAPL", OrderSide::Sell, "10", "5.00");
2584 assert_eq!(order.trail_price, Some("5.00".to_string()));
2585 assert_eq!(order.order_type, OrderType::TrailingStop);
2586 }
2587
2588 #[test]
2589 fn test_create_order_request_trailing_stop_percent() {
2590 let order = CreateOrderRequest::trailing_stop_percent("AAPL", OrderSide::Sell, "10", "2.5");
2591 assert_eq!(order.trail_percent, Some("2.5".to_string()));
2592 assert_eq!(order.order_type, OrderType::TrailingStop);
2593 }
2594
2595 #[test]
2596 fn test_create_order_request_bracket() {
2597 let tp = TakeProfit::new("160.00");
2598 let sl = StopLoss::new("140.00");
2599 let order =
2600 CreateOrderRequest::bracket("AAPL", OrderSide::Buy, "10", OrderType::Market, tp, sl);
2601
2602 assert_eq!(order.order_class, Some(OrderClass::Bracket));
2603 assert!(order.take_profit.is_some());
2604 assert!(order.stop_loss.is_some());
2605 assert_eq!(order.take_profit.unwrap().limit_price, "160.00");
2606 assert_eq!(order.stop_loss.unwrap().stop_price, "140.00");
2607 }
2608
2609 #[test]
2610 fn test_create_order_request_oco() {
2611 let tp = TakeProfit::new("160.00");
2612 let sl = StopLoss::with_limit("140.00", "139.50");
2613 let order = CreateOrderRequest::oco("AAPL", OrderSide::Sell, "10", tp, sl);
2614
2615 assert_eq!(order.order_class, Some(OrderClass::Oco));
2616 assert!(order.take_profit.is_some());
2617 assert!(order.stop_loss.is_some());
2618 }
2619
2620 #[test]
2621 fn test_create_order_request_oto() {
2622 let sl = StopLoss::new("140.00");
2623 let order = CreateOrderRequest::oto("AAPL", OrderSide::Buy, "10", OrderType::Limit, sl);
2624
2625 assert_eq!(order.order_class, Some(OrderClass::Oto));
2626 assert!(order.stop_loss.is_some());
2627 }
2628
2629 #[test]
2630 fn test_create_order_request_builder_methods() {
2631 let order = CreateOrderRequest::market("AAPL", OrderSide::Buy, "10")
2632 .time_in_force(TimeInForce::Gtc)
2633 .extended_hours(true)
2634 .client_order_id("my-order-123");
2635
2636 assert_eq!(order.time_in_force, TimeInForce::Gtc);
2637 assert_eq!(order.extended_hours, Some(true));
2638 assert_eq!(order.client_order_id, Some("my-order-123".to_string()));
2639 }
2640
2641 #[test]
2642 fn test_create_order_request_position_intent() {
2643 let order = CreateOrderRequest::market("AAPL", OrderSide::Buy, "10")
2644 .position_intent(PositionIntent::BuyToOpen);
2645
2646 assert_eq!(order.position_intent, Some(PositionIntent::BuyToOpen));
2647 }
2648
2649 #[test]
2650 fn test_replace_order_request_builder() {
2651 let request = ReplaceOrderRequest::new()
2652 .qty("20")
2653 .limit_price("155.00")
2654 .time_in_force(TimeInForce::Gtc);
2655
2656 assert_eq!(request.qty, Some("20".to_string()));
2657 assert_eq!(request.limit_price, Some("155.00".to_string()));
2658 assert_eq!(request.time_in_force, Some(TimeInForce::Gtc));
2659 }
2660
2661 #[test]
2662 fn test_order_params_builder() {
2663 let params = OrderParams::new()
2664 .status(OrderQueryStatus::Open)
2665 .limit(100)
2666 .nested(true)
2667 .symbols("AAPL,GOOGL")
2668 .side(OrderSide::Buy)
2669 .direction(SortDirection::Desc);
2670
2671 assert_eq!(params.status, Some(OrderQueryStatus::Open));
2672 assert_eq!(params.limit, Some(100));
2673 assert_eq!(params.nested, Some(true));
2674 assert_eq!(params.symbols, Some("AAPL,GOOGL".to_string()));
2675 assert_eq!(params.side, Some(OrderSide::Buy));
2676 assert_eq!(params.direction, Some(SortDirection::Desc));
2677 }
2678
2679 #[test]
2680 fn test_create_order_request_serialization() {
2681 let order = CreateOrderRequest::limit("AAPL", OrderSide::Buy, "10", "150.00")
2682 .client_order_id("test-123");
2683
2684 let json = serde_json::to_string(&order).unwrap();
2685 assert!(json.contains("\"symbol\":\"AAPL\""));
2686 assert!(json.contains("\"side\":\"buy\""));
2687 assert!(json.contains("\"type\":\"limit\""));
2688 assert!(json.contains("\"limit_price\":\"150.00\""));
2689 }
2690
2691 #[test]
2692 fn test_bracket_order_serialization() {
2693 let tp = TakeProfit::new("160.00");
2694 let sl = StopLoss::with_limit("140.00", "139.50");
2695 let order =
2696 CreateOrderRequest::bracket("AAPL", OrderSide::Buy, "10", OrderType::Limit, tp, sl)
2697 .with_limit_price("150.00");
2698
2699 let json = serde_json::to_string(&order).unwrap();
2700 assert!(json.contains("\"order_class\":\"bracket\""));
2701 assert!(json.contains("\"take_profit\""));
2702 assert!(json.contains("\"stop_loss\""));
2703 }
2704}