1#![allow(missing_docs)]
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9use uuid::Uuid;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum Environment {
14 Paper,
16 Live,
18}
19
20impl Environment {
21 #[must_use]
23 pub fn base_url(&self) -> &'static str {
24 match self {
25 Environment::Paper => "https://paper-api.alpaca.markets",
26 Environment::Live => "https://api.alpaca.markets",
27 }
28 }
29
30 #[must_use]
32 pub fn data_url(&self) -> &'static str {
33 "https://data.alpaca.markets"
34 }
35
36 #[must_use]
38 pub fn websocket_url(&self) -> &'static str {
39 match self {
40 Environment::Paper => "wss://paper-api.alpaca.markets/stream",
41 Environment::Live => "wss://api.alpaca.markets/stream",
42 }
43 }
44}
45
46#[derive(Debug, Serialize, Deserialize, Clone)]
48pub struct Account {
49 pub id: Uuid,
51 pub account_number: String,
53 pub status: AccountStatus,
55 pub currency: String,
57 pub buying_power: String,
59 pub regt_buying_power: String,
61 pub daytrading_buying_power: String,
63 pub cash: String,
65 pub portfolio_value: String,
67 pub pattern_day_trader: bool,
69 pub trading_blocked: bool,
71 pub transfers_blocked: bool,
73 pub account_blocked: bool,
75 pub created_at: DateTime<Utc>,
77 pub trade_suspended_by_user: bool,
79 pub multiplier: String,
81 pub shorting_enabled: bool,
83 pub equity: String,
85 pub last_equity: String,
87 pub long_market_value: String,
89 pub short_market_value: String,
91 pub initial_margin: String,
93 pub maintenance_margin: String,
95 pub last_maintenance_margin: String,
97 pub sma: String,
99 pub daytrade_count: i32,
101}
102
103#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
105#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
106pub enum AccountStatus {
107 Onboarding,
109 SubmissionFailed,
111 Submitted,
113 AccountUpdated,
115 ApprovalPending,
117 Active,
119 Rejected,
121 Disabled,
123 AccountClosed,
125}
126
127#[derive(Debug, Serialize, Deserialize, Clone)]
129pub struct Asset {
130 pub id: Uuid,
132 pub class: AssetClass,
134 pub exchange: String,
136 pub symbol: String,
138 pub name: String,
140 pub status: AssetStatus,
142 pub tradable: bool,
144 pub marginable: bool,
146 pub shortable: bool,
148 pub easy_to_borrow: bool,
150 pub fractionable: bool,
152}
153
154#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
156#[serde(rename_all = "snake_case")]
157pub enum AssetClass {
158 UsEquity,
160 Crypto,
162}
163
164#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
166#[serde(rename_all = "snake_case")]
167pub enum AssetStatus {
168 Active,
170 Inactive,
172}
173
174#[derive(Debug, Serialize, Deserialize, Clone)]
176pub struct Order {
177 pub id: Uuid,
179 pub client_order_id: String,
181 pub created_at: DateTime<Utc>,
183 pub updated_at: DateTime<Utc>,
185 pub submitted_at: Option<DateTime<Utc>>,
187 pub filled_at: Option<DateTime<Utc>>,
189 pub expired_at: Option<DateTime<Utc>>,
191 pub canceled_at: Option<DateTime<Utc>>,
193 pub failed_at: Option<DateTime<Utc>>,
195 pub replaced_at: Option<DateTime<Utc>>,
197 pub replaced_by: Option<Uuid>,
199 pub replaces: Option<Uuid>,
201 pub asset_id: Uuid,
203 pub symbol: String,
205 pub asset_class: AssetClass,
207 pub notional: Option<String>,
209 pub qty: Option<String>,
211 pub filled_qty: String,
213 pub filled_avg_price: Option<String>,
215 pub order_class: OrderClass,
217 pub order_type: OrderType,
219 pub side: OrderSide,
221 pub time_in_force: TimeInForce,
223 pub limit_price: Option<String>,
225 pub stop_price: Option<String>,
227 pub status: OrderStatus,
229 pub extended_hours: bool,
231 pub legs: Option<Vec<Order>>,
233 pub trail_percent: Option<String>,
235 pub trail_price: Option<String>,
237 pub hwm: Option<String>,
239}
240
241#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
243#[serde(rename_all = "snake_case")]
244pub enum OrderClass {
245 Simple,
247 Bracket,
249 Oco,
251 Oto,
253}
254
255#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
257#[serde(rename_all = "snake_case")]
258pub enum OrderType {
259 #[default]
261 Market,
262 Limit,
264 Stop,
266 StopLimit,
268 TrailingStop,
270}
271
272#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
274#[serde(rename_all = "snake_case")]
275pub enum OrderSide {
276 #[default]
278 Buy,
279 Sell,
281}
282
283#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Default)]
287#[serde(rename_all = "lowercase")]
288pub enum TimeInForce {
289 #[default]
291 Day,
292 Gtc,
294 Ioc,
296 Fok,
298 Opg,
300 Cls,
302 Gtd,
304}
305
306#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
308#[serde(rename_all = "snake_case")]
309pub enum OrderStatus {
310 New,
311 PartiallyFilled,
312 Filled,
313 DoneForDay,
314 Canceled,
315 Expired,
316 Replaced,
317 PendingCancel,
318 PendingReplace,
319 PendingReview,
320 Accepted,
321 PendingNew,
322 AcceptedForBidding,
323 Stopped,
324 Rejected,
325 Suspended,
326 Calculated,
327}
328
329#[derive(Debug, Serialize, Deserialize, Clone)]
331pub struct Position {
332 pub asset_id: Uuid,
333 pub symbol: String,
334 pub exchange: String,
335 pub asset_class: AssetClass,
336 pub avg_entry_price: String,
337 pub qty: String,
338 pub side: PositionSide,
339 pub market_value: String,
340 pub cost_basis: String,
341 pub unrealized_pl: String,
342 pub unrealized_plpc: String,
343 pub unrealized_intraday_pl: String,
344 pub unrealized_intraday_plpc: String,
345 pub current_price: String,
346 pub lastday_price: String,
347 pub change_today: String,
348}
349
350#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
352#[serde(rename_all = "snake_case")]
353pub enum PositionSide {
354 Long,
355 Short,
356}
357
358#[derive(Debug, Serialize, Deserialize, Clone)]
360pub struct Bar {
361 pub timestamp: DateTime<Utc>,
362 pub open: f64,
363 pub high: f64,
364 pub low: f64,
365 pub close: f64,
366 pub volume: u64,
367 pub trade_count: Option<u64>,
368 pub vwap: Option<f64>,
369}
370
371#[derive(Debug, Serialize, Deserialize, Clone)]
373pub struct Quote {
374 pub timestamp: DateTime<Utc>,
375 pub timeframe: String,
376 pub bid_price: f64,
377 pub bid_size: u32,
378 pub ask_price: f64,
379 pub ask_size: u32,
380 pub bid_exchange: String,
381 pub ask_exchange: String,
382}
383
384#[derive(Debug, Serialize, Deserialize, Clone)]
386pub struct Trade {
387 pub timestamp: DateTime<Utc>,
388 pub price: f64,
389 pub size: u32,
390 pub exchange: String,
391 pub conditions: Vec<String>,
392 pub id: u64,
393}
394
395#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
397#[serde(rename_all = "snake_case")]
398pub enum Timeframe {
399 #[serde(rename = "1Min")]
400 OneMinute,
401 #[serde(rename = "5Min")]
402 FiveMinutes,
403 #[serde(rename = "15Min")]
404 FifteenMinutes,
405 #[serde(rename = "30Min")]
406 ThirtyMinutes,
407 #[serde(rename = "1Hour")]
408 OneHour,
409 #[serde(rename = "1Day")]
410 OneDay,
411 #[serde(rename = "1Week")]
412 OneWeek,
413 #[serde(rename = "1Month")]
414 OneMonth,
415}
416
417#[derive(Debug, Serialize, Deserialize, Clone)]
419pub struct Watchlist {
420 pub id: Uuid,
421 pub account_id: Uuid,
422 pub created_at: DateTime<Utc>,
423 pub updated_at: DateTime<Utc>,
424 pub name: String,
425 pub assets: Vec<Asset>,
426}
427
428#[derive(Debug, Serialize, Deserialize, Clone)]
430pub struct Calendar {
431 pub date: String,
432 pub open: String,
433 pub close: String,
434 pub session_open: String,
435 pub session_close: String,
436}
437
438#[derive(Debug, Serialize, Deserialize, Clone)]
440pub struct Clock {
441 pub timestamp: DateTime<Utc>,
442 pub is_open: bool,
443 pub next_open: DateTime<Utc>,
444 pub next_close: DateTime<Utc>,
445}
446
447#[derive(Debug, Serialize, Deserialize, Clone)]
449pub struct PortfolioHistory {
450 pub timestamp: Vec<i64>,
451 pub equity: Vec<Option<f64>>,
452 pub profit_loss: Vec<Option<f64>>,
453 pub profit_loss_pct: Vec<Option<f64>>,
454 pub base_value: f64,
455 pub timeframe: String,
456}
457
458#[derive(Debug, Serialize, Deserialize, Clone)]
460pub struct AccountActivity {
461 pub id: String,
462 pub account_id: Uuid,
463 pub activity_type: ActivityType,
464 pub date: String,
465 pub net_amount: String,
466 pub symbol: Option<String>,
467 pub qty: Option<String>,
468 pub per_share_amount: Option<String>,
469}
470
471#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
473#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
474pub enum ActivityType {
475 Fill,
476 TransactionFee,
477 Misc,
478 AcatsIn,
479 AcatsOut,
480 Csd,
481 Csr,
482 Div,
483 Divcgl,
484 Divcgs,
485 Divfee,
486 Divft,
487 Divnra,
488 Divroc,
489 Divtw,
490 Divtxex,
491 Int,
492 Jnlc,
493 Jnls,
494 Ma,
495 Nc,
496 Opasn,
497 Opexp,
498 Opxrc,
499 Pta,
500 Ptc,
501 Reorg,
502 Sc,
503 Sso,
504 Tc,
505}
506
507#[derive(Debug, Serialize, Deserialize, Clone)]
509pub struct NewsArticle {
510 pub id: i64,
511 pub headline: String,
512 pub author: String,
513 pub created_at: DateTime<Utc>,
514 pub updated_at: DateTime<Utc>,
515 pub summary: String,
516 pub content: String,
517 pub url: String,
518 pub symbols: Vec<String>,
519}
520
521#[derive(Debug, Serialize, Deserialize, Clone)]
523pub struct CryptoWallet {
524 pub id: Uuid,
525 pub name: String,
526 pub currency: String,
527 pub balance: String,
528 pub available_balance: String,
529 pub created_at: DateTime<Utc>,
530 pub updated_at: DateTime<Utc>,
531}
532
533#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
537#[serde(rename_all = "snake_case")]
538pub enum PositionIntent {
539 BuyToOpen,
541 BuyToClose,
543 SellToOpen,
545 SellToClose,
547}
548
549#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
551pub struct TakeProfit {
552 pub limit_price: String,
554}
555
556impl TakeProfit {
557 #[must_use]
559 pub fn new(limit_price: impl Into<String>) -> Self {
560 Self {
561 limit_price: limit_price.into(),
562 }
563 }
564}
565
566#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
568pub struct StopLoss {
569 pub stop_price: String,
571 pub limit_price: Option<String>,
573}
574
575impl StopLoss {
576 #[must_use]
578 pub fn new(stop_price: impl Into<String>) -> Self {
579 Self {
580 stop_price: stop_price.into(),
581 limit_price: None,
582 }
583 }
584
585 #[must_use]
587 pub fn with_limit(stop_price: impl Into<String>, limit_price: impl Into<String>) -> Self {
588 Self {
589 stop_price: stop_price.into(),
590 limit_price: Some(limit_price.into()),
591 }
592 }
593}
594
595#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
597#[serde(rename_all = "lowercase")]
598pub enum SortDirection {
599 Asc,
601 Desc,
603}
604
605#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
607#[serde(rename_all = "lowercase")]
608pub enum OrderQueryStatus {
609 Open,
611 Closed,
613 All,
615}
616
617#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
623#[serde(rename_all = "lowercase")]
624pub enum OptionType {
625 Call,
627 Put,
629}
630
631#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
633#[serde(rename_all = "lowercase")]
634pub enum OptionStyle {
635 American,
637 European,
639}
640
641#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
643#[serde(rename_all = "snake_case")]
644pub enum OptionsApprovalLevel {
645 #[serde(rename = "1")]
647 Level1,
648 #[serde(rename = "2")]
650 Level2,
651 #[serde(rename = "3")]
653 Level3,
654 Disabled,
656}
657
658#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
660#[serde(rename_all = "snake_case")]
661pub enum OptionsApprovalStatus {
662 Pending,
664 Approved,
666 Rejected,
668 Inactive,
670}
671
672#[derive(Debug, Serialize, Deserialize, Clone)]
674pub struct OptionContract {
675 pub id: Uuid,
677 pub symbol: String,
679 pub name: String,
681 pub status: AssetStatus,
683 pub tradable: bool,
685 pub expiration_date: String,
687 pub strike_price: String,
689 #[serde(rename = "type")]
691 pub option_type: OptionType,
692 pub style: OptionStyle,
694 pub underlying_symbol: String,
696 pub underlying_asset_id: Uuid,
698 pub root_symbol: String,
700 #[serde(default)]
702 pub open_interest: Option<String>,
703 #[serde(default)]
705 pub open_interest_date: Option<String>,
706 #[serde(default)]
708 pub size: Option<String>,
709 #[serde(default)]
711 pub close_price: Option<String>,
712 #[serde(default)]
714 pub close_price_date: Option<String>,
715}
716
717#[derive(Debug, Serialize, Deserialize, Clone)]
719pub struct OptionGreeks {
720 pub delta: Option<f64>,
722 pub gamma: Option<f64>,
724 pub theta: Option<f64>,
726 pub vega: Option<f64>,
728 pub rho: Option<f64>,
730}
731
732#[derive(Debug, Serialize, Deserialize, Clone)]
734pub struct OptionQuote {
735 #[serde(rename = "t")]
737 pub timestamp: DateTime<Utc>,
738 #[serde(rename = "bp")]
740 pub bid_price: f64,
741 #[serde(rename = "bs")]
743 pub bid_size: u64,
744 #[serde(rename = "ap")]
746 pub ask_price: f64,
747 #[serde(rename = "as")]
749 pub ask_size: u64,
750 #[serde(rename = "bx")]
752 pub bid_exchange: String,
753 #[serde(rename = "ax")]
755 pub ask_exchange: String,
756 #[serde(rename = "c", default)]
758 pub conditions: Option<String>,
759}
760
761#[derive(Debug, Serialize, Deserialize, Clone)]
763pub struct OptionTrade {
764 #[serde(rename = "t")]
766 pub timestamp: DateTime<Utc>,
767 #[serde(rename = "p")]
769 pub price: f64,
770 #[serde(rename = "s")]
772 pub size: u64,
773 #[serde(rename = "x")]
775 pub exchange: String,
776 #[serde(rename = "c", default)]
778 pub conditions: Option<String>,
779}
780
781#[derive(Debug, Serialize, Deserialize, Clone)]
783pub struct OptionBar {
784 #[serde(rename = "t")]
786 pub timestamp: DateTime<Utc>,
787 #[serde(rename = "o")]
789 pub open: f64,
790 #[serde(rename = "h")]
792 pub high: f64,
793 #[serde(rename = "l")]
795 pub low: f64,
796 #[serde(rename = "c")]
798 pub close: f64,
799 #[serde(rename = "v")]
801 pub volume: u64,
802 #[serde(rename = "n", default)]
804 pub trade_count: Option<u64>,
805 #[serde(rename = "vw", default)]
807 pub vwap: Option<f64>,
808}
809
810#[derive(Debug, Serialize, Deserialize, Clone)]
812pub struct OptionSnapshot {
813 #[serde(rename = "latestQuote")]
815 pub latest_quote: Option<OptionQuote>,
816 #[serde(rename = "latestTrade")]
818 pub latest_trade: Option<OptionTrade>,
819 pub greeks: Option<OptionGreeks>,
821 #[serde(rename = "impliedVolatility")]
823 pub implied_volatility: Option<f64>,
824}
825
826#[derive(Debug, Serialize, Deserialize, Clone)]
828pub struct OptionChainEntry {
829 pub contract: OptionContract,
831 pub snapshot: Option<OptionSnapshot>,
833}
834
835#[derive(Debug, Serialize, Deserialize, Clone)]
837pub struct OptionExerciseRequest {
838 pub symbol: String,
840 #[serde(default)]
842 pub qty: Option<String>,
843}
844
845#[derive(Debug, Serialize, Deserialize, Clone)]
847pub struct OptionsApprovalRequest {
848 pub options_trading_level: OptionsApprovalLevel,
850}
851
852#[derive(Debug, Serialize, Deserialize, Clone)]
854pub struct OptionsApproval {
855 pub options_trading_level: Option<OptionsApprovalLevel>,
857 pub status: OptionsApprovalStatus,
859 #[serde(default)]
861 pub reason: Option<String>,
862}
863
864#[derive(Debug, Serialize, Deserialize, Clone, Default)]
866pub struct OptionContractParams {
867 #[serde(skip_serializing_if = "Option::is_none")]
869 pub underlying_symbol: Option<String>,
870 #[serde(skip_serializing_if = "Option::is_none")]
872 pub expiration_date: Option<String>,
873 #[serde(skip_serializing_if = "Option::is_none")]
875 pub expiration_date_gte: Option<String>,
876 #[serde(skip_serializing_if = "Option::is_none")]
878 pub expiration_date_lte: Option<String>,
879 #[serde(skip_serializing_if = "Option::is_none")]
881 pub strike_price_gte: Option<String>,
882 #[serde(skip_serializing_if = "Option::is_none")]
884 pub strike_price_lte: Option<String>,
885 #[serde(skip_serializing_if = "Option::is_none")]
887 #[serde(rename = "type")]
888 pub option_type: Option<OptionType>,
889 #[serde(skip_serializing_if = "Option::is_none")]
891 pub root_symbol: Option<String>,
892 #[serde(skip_serializing_if = "Option::is_none")]
894 pub style: Option<OptionStyle>,
895 #[serde(skip_serializing_if = "Option::is_none")]
897 pub limit: Option<u32>,
898 #[serde(skip_serializing_if = "Option::is_none")]
900 pub page_token: Option<String>,
901}
902
903impl OptionContractParams {
904 #[must_use]
906 pub fn new() -> Self {
907 Self::default()
908 }
909
910 #[must_use]
912 pub fn underlying_symbol(mut self, symbol: &str) -> Self {
913 self.underlying_symbol = Some(symbol.to_string());
914 self
915 }
916
917 #[must_use]
919 pub fn expiration_date(mut self, date: &str) -> Self {
920 self.expiration_date = Some(date.to_string());
921 self
922 }
923
924 #[must_use]
926 pub fn option_type(mut self, option_type: OptionType) -> Self {
927 self.option_type = Some(option_type);
928 self
929 }
930
931 #[must_use]
933 pub fn strike_price_range(mut self, min: &str, max: &str) -> Self {
934 self.strike_price_gte = Some(min.to_string());
935 self.strike_price_lte = Some(max.to_string());
936 self
937 }
938
939 #[must_use]
941 pub fn limit(mut self, limit: u32) -> Self {
942 self.limit = Some(limit);
943 self
944 }
945}
946
947#[derive(Debug, Serialize, Deserialize, Clone, Default)]
949pub struct OptionBarsParams {
950 #[serde(skip_serializing_if = "Option::is_none")]
952 pub symbols: Option<String>,
953 #[serde(skip_serializing_if = "Option::is_none")]
955 pub timeframe: Option<String>,
956 #[serde(skip_serializing_if = "Option::is_none")]
958 pub start: Option<String>,
959 #[serde(skip_serializing_if = "Option::is_none")]
961 pub end: Option<String>,
962 #[serde(skip_serializing_if = "Option::is_none")]
964 pub limit: Option<u32>,
965 #[serde(skip_serializing_if = "Option::is_none")]
967 pub page_token: Option<String>,
968}
969
970impl OptionBarsParams {
971 #[must_use]
973 pub fn new(symbols: &str) -> Self {
974 Self {
975 symbols: Some(symbols.to_string()),
976 ..Default::default()
977 }
978 }
979
980 #[must_use]
982 pub fn timeframe(mut self, timeframe: &str) -> Self {
983 self.timeframe = Some(timeframe.to_string());
984 self
985 }
986
987 #[must_use]
989 pub fn time_range(mut self, start: &str, end: &str) -> Self {
990 self.start = Some(start.to_string());
991 self.end = Some(end.to_string());
992 self
993 }
994
995 #[must_use]
997 pub fn limit(mut self, limit: u32) -> Self {
998 self.limit = Some(limit);
999 self
1000 }
1001}
1002
1003#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1009#[serde(rename_all = "lowercase")]
1010pub enum DataFeed {
1011 Iex,
1013 Sip,
1015 Otc,
1017}
1018
1019#[derive(Debug, Serialize, Deserialize, Clone)]
1021pub struct StockSnapshot {
1022 #[serde(rename = "latestTrade")]
1024 pub latest_trade: Option<Trade>,
1025 #[serde(rename = "latestQuote")]
1027 pub latest_quote: Option<Quote>,
1028 #[serde(rename = "minuteBar")]
1030 pub minute_bar: Option<Bar>,
1031 #[serde(rename = "dailyBar")]
1033 pub daily_bar: Option<Bar>,
1034 #[serde(rename = "prevDailyBar")]
1036 pub prev_daily_bar: Option<Bar>,
1037}
1038
1039#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1041#[serde(rename_all = "snake_case")]
1042pub enum CorporateActionType {
1043 Dividend,
1045 Split,
1047 ReverseSplit,
1049 Spinoff,
1051 Merger,
1053 Rights,
1055 StockDividend,
1057 Redemption,
1059 NameChange,
1061 SymbolChange,
1063 Worthless,
1065}
1066
1067#[derive(Debug, Serialize, Deserialize, Clone)]
1069pub struct CorporateAction {
1070 pub id: String,
1072 #[serde(rename = "ca_type")]
1074 pub action_type: CorporateActionType,
1075 #[serde(rename = "ca_sub_type")]
1077 pub sub_type: Option<String>,
1078 pub initiating_symbol: Option<String>,
1080 pub initiating_original_cusip: Option<String>,
1082 pub target_symbol: Option<String>,
1084 pub target_original_cusip: Option<String>,
1086 pub declaration_date: Option<String>,
1088 pub ex_date: Option<String>,
1090 pub record_date: Option<String>,
1092 pub payable_date: Option<String>,
1094 pub cash: Option<String>,
1096 pub old_rate: Option<String>,
1098 pub new_rate: Option<String>,
1100}
1101
1102#[derive(Debug, Serialize, Deserialize, Clone)]
1104pub struct Luld {
1105 #[serde(rename = "i")]
1107 pub indicator: String,
1108 #[serde(rename = "u")]
1110 pub limit_up_price: f64,
1111 #[serde(rename = "d")]
1113 pub limit_down_price: f64,
1114 #[serde(rename = "t")]
1116 pub timestamp: DateTime<Utc>,
1117}
1118
1119#[derive(Debug, Serialize, Deserialize, Clone)]
1121pub struct TradingStatus {
1122 #[serde(rename = "sc")]
1124 pub status_code: String,
1125 #[serde(rename = "sm")]
1127 pub status_message: String,
1128 #[serde(rename = "rc")]
1130 pub reason_code: String,
1131 #[serde(rename = "rm")]
1133 pub reason_message: String,
1134 #[serde(rename = "t")]
1136 pub timestamp: DateTime<Utc>,
1137}
1138
1139#[derive(Debug, Serialize, Deserialize, Clone)]
1141pub struct Auction {
1142 #[serde(rename = "at")]
1144 pub auction_type: String,
1145 #[serde(rename = "ap")]
1147 pub price: Option<f64>,
1148 #[serde(rename = "as")]
1150 pub size: Option<u64>,
1151 #[serde(rename = "t")]
1153 pub timestamp: DateTime<Utc>,
1154}
1155
1156#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1158pub struct MultiBarsParams {
1159 #[serde(skip_serializing_if = "Option::is_none")]
1161 pub symbols: Option<String>,
1162 #[serde(skip_serializing_if = "Option::is_none")]
1164 pub timeframe: Option<String>,
1165 #[serde(skip_serializing_if = "Option::is_none")]
1167 pub start: Option<String>,
1168 #[serde(skip_serializing_if = "Option::is_none")]
1170 pub end: Option<String>,
1171 #[serde(skip_serializing_if = "Option::is_none")]
1173 pub limit: Option<u32>,
1174 #[serde(skip_serializing_if = "Option::is_none")]
1176 pub feed: Option<DataFeed>,
1177 #[serde(skip_serializing_if = "Option::is_none")]
1179 pub page_token: Option<String>,
1180}
1181
1182impl MultiBarsParams {
1183 #[must_use]
1185 pub fn new(symbols: &str) -> Self {
1186 Self {
1187 symbols: Some(symbols.to_string()),
1188 ..Default::default()
1189 }
1190 }
1191
1192 #[must_use]
1194 pub fn timeframe(mut self, timeframe: &str) -> Self {
1195 self.timeframe = Some(timeframe.to_string());
1196 self
1197 }
1198
1199 #[must_use]
1201 pub fn time_range(mut self, start: &str, end: &str) -> Self {
1202 self.start = Some(start.to_string());
1203 self.end = Some(end.to_string());
1204 self
1205 }
1206
1207 #[must_use]
1209 pub fn feed(mut self, feed: DataFeed) -> Self {
1210 self.feed = Some(feed);
1211 self
1212 }
1213
1214 #[must_use]
1216 pub fn limit(mut self, limit: u32) -> Self {
1217 self.limit = Some(limit);
1218 self
1219 }
1220}
1221
1222#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1224pub struct MultiQuotesParams {
1225 #[serde(skip_serializing_if = "Option::is_none")]
1227 pub symbols: Option<String>,
1228 #[serde(skip_serializing_if = "Option::is_none")]
1230 pub start: Option<String>,
1231 #[serde(skip_serializing_if = "Option::is_none")]
1233 pub end: Option<String>,
1234 #[serde(skip_serializing_if = "Option::is_none")]
1236 pub limit: Option<u32>,
1237 #[serde(skip_serializing_if = "Option::is_none")]
1239 pub feed: Option<DataFeed>,
1240 #[serde(skip_serializing_if = "Option::is_none")]
1242 pub page_token: Option<String>,
1243}
1244
1245impl MultiQuotesParams {
1246 #[must_use]
1248 pub fn new(symbols: &str) -> Self {
1249 Self {
1250 symbols: Some(symbols.to_string()),
1251 ..Default::default()
1252 }
1253 }
1254
1255 #[must_use]
1257 pub fn time_range(mut self, start: &str, end: &str) -> Self {
1258 self.start = Some(start.to_string());
1259 self.end = Some(end.to_string());
1260 self
1261 }
1262
1263 #[must_use]
1265 pub fn feed(mut self, feed: DataFeed) -> Self {
1266 self.feed = Some(feed);
1267 self
1268 }
1269
1270 #[must_use]
1272 pub fn limit(mut self, limit: u32) -> Self {
1273 self.limit = Some(limit);
1274 self
1275 }
1276}
1277
1278#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1280pub struct MultiTradesParams {
1281 #[serde(skip_serializing_if = "Option::is_none")]
1283 pub symbols: Option<String>,
1284 #[serde(skip_serializing_if = "Option::is_none")]
1286 pub start: Option<String>,
1287 #[serde(skip_serializing_if = "Option::is_none")]
1289 pub end: Option<String>,
1290 #[serde(skip_serializing_if = "Option::is_none")]
1292 pub limit: Option<u32>,
1293 #[serde(skip_serializing_if = "Option::is_none")]
1295 pub feed: Option<DataFeed>,
1296 #[serde(skip_serializing_if = "Option::is_none")]
1298 pub page_token: Option<String>,
1299}
1300
1301impl MultiTradesParams {
1302 #[must_use]
1304 pub fn new(symbols: &str) -> Self {
1305 Self {
1306 symbols: Some(symbols.to_string()),
1307 ..Default::default()
1308 }
1309 }
1310
1311 #[must_use]
1313 pub fn time_range(mut self, start: &str, end: &str) -> Self {
1314 self.start = Some(start.to_string());
1315 self.end = Some(end.to_string());
1316 self
1317 }
1318
1319 #[must_use]
1321 pub fn feed(mut self, feed: DataFeed) -> Self {
1322 self.feed = Some(feed);
1323 self
1324 }
1325
1326 #[must_use]
1328 pub fn limit(mut self, limit: u32) -> Self {
1329 self.limit = Some(limit);
1330 self
1331 }
1332}
1333
1334#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1336pub struct CorporateActionsParams {
1337 #[serde(skip_serializing_if = "Option::is_none")]
1339 pub symbols: Option<String>,
1340 #[serde(skip_serializing_if = "Option::is_none")]
1342 pub types: Option<String>,
1343 #[serde(skip_serializing_if = "Option::is_none")]
1345 pub start: Option<String>,
1346 #[serde(skip_serializing_if = "Option::is_none")]
1348 pub end: Option<String>,
1349 #[serde(skip_serializing_if = "Option::is_none")]
1351 pub limit: Option<u32>,
1352 #[serde(skip_serializing_if = "Option::is_none")]
1354 pub page_token: Option<String>,
1355}
1356
1357impl CorporateActionsParams {
1358 #[must_use]
1360 pub fn new() -> Self {
1361 Self::default()
1362 }
1363
1364 #[must_use]
1366 pub fn symbols(mut self, symbols: &str) -> Self {
1367 self.symbols = Some(symbols.to_string());
1368 self
1369 }
1370
1371 #[must_use]
1373 pub fn types(mut self, types: &str) -> Self {
1374 self.types = Some(types.to_string());
1375 self
1376 }
1377
1378 #[must_use]
1380 pub fn date_range(mut self, start: &str, end: &str) -> Self {
1381 self.start = Some(start.to_string());
1382 self.end = Some(end.to_string());
1383 self
1384 }
1385
1386 #[must_use]
1388 pub fn limit(mut self, limit: u32) -> Self {
1389 self.limit = Some(limit);
1390 self
1391 }
1392}
1393
1394#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1400#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1401pub enum BrokerAccountStatus {
1402 Onboarding,
1404 SubmissionFailed,
1406 Submitted,
1408 ActionRequired,
1410 Active,
1412 Rejected,
1414 Approved,
1416 Disabled,
1418 AccountClosed,
1420}
1421
1422#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1424#[serde(rename_all = "snake_case")]
1425pub enum AgreementType {
1426 MarginAgreement,
1428 AccountAgreement,
1430 CustomerAgreement,
1432 CryptoAgreement,
1434 OptionsAgreement,
1436}
1437
1438#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1440#[serde(rename_all = "snake_case")]
1441pub enum FundingSource {
1442 EmploymentIncome,
1444 Investments,
1446 Inheritance,
1448 BusinessIncome,
1450 Savings,
1452 Family,
1454}
1455
1456#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1458#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1459pub enum TaxIdType {
1460 UsaSsn,
1462 ArgArCuit,
1464 AusTfn,
1466 AusAbn,
1468 BraCpf,
1470 CanSin,
1472 ChlRut,
1474 ColNit,
1476 DeuTaxId,
1478 EspNie,
1480 FraSpi,
1482 GbrNino,
1484 GbrUtr,
1486 HkgHkid,
1488 HunTin,
1490 IndPan,
1492 IsrId,
1494 ItaCf,
1496 JpnMyNumber,
1498 KorRrn,
1500 MexRfc,
1502 NldBsn,
1504 NzlIrd,
1506 PolPesel,
1508 SwePn,
1510 SgpNric,
1512 TwnId,
1514 NotApplicable,
1516}
1517
1518#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1520pub struct Contact {
1521 pub email_address: String,
1523 #[serde(skip_serializing_if = "Option::is_none")]
1525 pub phone_number: Option<String>,
1526 pub street_address: Vec<String>,
1528 pub city: String,
1530 #[serde(skip_serializing_if = "Option::is_none")]
1532 pub state: Option<String>,
1533 pub postal_code: String,
1535 pub country: String,
1537}
1538
1539impl Contact {
1540 #[must_use]
1542 pub fn new(email: &str, city: &str, postal_code: &str, country: &str) -> Self {
1543 Self {
1544 email_address: email.to_string(),
1545 phone_number: None,
1546 street_address: Vec::new(),
1547 city: city.to_string(),
1548 state: None,
1549 postal_code: postal_code.to_string(),
1550 country: country.to_string(),
1551 }
1552 }
1553
1554 #[must_use]
1556 pub fn phone(mut self, phone: &str) -> Self {
1557 self.phone_number = Some(phone.to_string());
1558 self
1559 }
1560
1561 #[must_use]
1563 pub fn street(mut self, street: &str) -> Self {
1564 self.street_address.push(street.to_string());
1565 self
1566 }
1567
1568 #[must_use]
1570 pub fn state(mut self, state: &str) -> Self {
1571 self.state = Some(state.to_string());
1572 self
1573 }
1574}
1575
1576#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1578pub struct Identity {
1579 pub given_name: String,
1581 pub family_name: String,
1583 #[serde(skip_serializing_if = "Option::is_none")]
1585 pub middle_name: Option<String>,
1586 pub date_of_birth: String,
1588 #[serde(skip_serializing_if = "Option::is_none")]
1590 pub tax_id: Option<String>,
1591 #[serde(skip_serializing_if = "Option::is_none")]
1593 pub tax_id_type: Option<TaxIdType>,
1594 #[serde(skip_serializing_if = "Option::is_none")]
1596 pub country_of_citizenship: Option<String>,
1597 #[serde(skip_serializing_if = "Option::is_none")]
1599 pub country_of_birth: Option<String>,
1600 #[serde(skip_serializing_if = "Option::is_none")]
1602 pub country_of_tax_residence: Option<String>,
1603 #[serde(skip_serializing_if = "Option::is_none")]
1605 pub funding_source: Option<Vec<FundingSource>>,
1606 #[serde(skip_serializing_if = "Option::is_none")]
1608 pub annual_income_min: Option<String>,
1609 #[serde(skip_serializing_if = "Option::is_none")]
1611 pub annual_income_max: Option<String>,
1612 #[serde(skip_serializing_if = "Option::is_none")]
1614 pub liquid_net_worth_min: Option<String>,
1615 #[serde(skip_serializing_if = "Option::is_none")]
1617 pub liquid_net_worth_max: Option<String>,
1618 #[serde(skip_serializing_if = "Option::is_none")]
1620 pub total_net_worth_min: Option<String>,
1621 #[serde(skip_serializing_if = "Option::is_none")]
1623 pub total_net_worth_max: Option<String>,
1624}
1625
1626impl Identity {
1627 #[must_use]
1629 pub fn new(given_name: &str, family_name: &str, date_of_birth: &str) -> Self {
1630 Self {
1631 given_name: given_name.to_string(),
1632 family_name: family_name.to_string(),
1633 date_of_birth: date_of_birth.to_string(),
1634 ..Default::default()
1635 }
1636 }
1637
1638 #[must_use]
1640 pub fn tax_id(mut self, tax_id: &str, tax_id_type: TaxIdType) -> Self {
1641 self.tax_id = Some(tax_id.to_string());
1642 self.tax_id_type = Some(tax_id_type);
1643 self
1644 }
1645
1646 #[must_use]
1648 pub fn citizenship(mut self, country: &str) -> Self {
1649 self.country_of_citizenship = Some(country.to_string());
1650 self
1651 }
1652
1653 #[must_use]
1655 pub fn funding_sources(mut self, sources: Vec<FundingSource>) -> Self {
1656 self.funding_source = Some(sources);
1657 self
1658 }
1659}
1660
1661#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1663pub struct Disclosures {
1664 pub is_control_person: bool,
1666 pub is_affiliated_exchange_or_finra: bool,
1668 pub is_politically_exposed: bool,
1670 pub immediate_family_exposed: bool,
1672 #[serde(skip_serializing_if = "Option::is_none")]
1674 pub employment_status: Option<String>,
1675 #[serde(skip_serializing_if = "Option::is_none")]
1677 pub employer_name: Option<String>,
1678 #[serde(skip_serializing_if = "Option::is_none")]
1680 pub employer_address: Option<String>,
1681 #[serde(skip_serializing_if = "Option::is_none")]
1683 pub employment_position: Option<String>,
1684}
1685
1686impl Disclosures {
1687 #[must_use]
1689 pub fn new() -> Self {
1690 Self::default()
1691 }
1692
1693 #[must_use]
1695 pub fn control_person(mut self, is_control: bool) -> Self {
1696 self.is_control_person = is_control;
1697 self
1698 }
1699
1700 #[must_use]
1702 pub fn employment(mut self, status: &str, employer: &str, position: &str) -> Self {
1703 self.employment_status = Some(status.to_string());
1704 self.employer_name = Some(employer.to_string());
1705 self.employment_position = Some(position.to_string());
1706 self
1707 }
1708}
1709
1710#[derive(Debug, Serialize, Deserialize, Clone)]
1712pub struct Agreement {
1713 pub agreement: AgreementType,
1715 pub signed_at: String,
1717 pub ip_address: String,
1719 #[serde(skip_serializing_if = "Option::is_none")]
1721 pub revision: Option<String>,
1722}
1723
1724impl Agreement {
1725 #[must_use]
1727 pub fn new(agreement_type: AgreementType, signed_at: &str, ip_address: &str) -> Self {
1728 Self {
1729 agreement: agreement_type,
1730 signed_at: signed_at.to_string(),
1731 ip_address: ip_address.to_string(),
1732 revision: None,
1733 }
1734 }
1735}
1736
1737#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1739pub struct TrustedContact {
1740 pub given_name: String,
1742 pub family_name: String,
1744 #[serde(skip_serializing_if = "Option::is_none")]
1746 pub email_address: Option<String>,
1747 #[serde(skip_serializing_if = "Option::is_none")]
1749 pub phone_number: Option<String>,
1750 #[serde(skip_serializing_if = "Option::is_none")]
1752 pub street_address: Option<Vec<String>>,
1753 #[serde(skip_serializing_if = "Option::is_none")]
1755 pub city: Option<String>,
1756 #[serde(skip_serializing_if = "Option::is_none")]
1758 pub state: Option<String>,
1759 #[serde(skip_serializing_if = "Option::is_none")]
1761 pub postal_code: Option<String>,
1762 #[serde(skip_serializing_if = "Option::is_none")]
1764 pub country: Option<String>,
1765}
1766
1767impl TrustedContact {
1768 #[must_use]
1770 pub fn new(given_name: &str, family_name: &str) -> Self {
1771 Self {
1772 given_name: given_name.to_string(),
1773 family_name: family_name.to_string(),
1774 ..Default::default()
1775 }
1776 }
1777
1778 #[must_use]
1780 pub fn email(mut self, email: &str) -> Self {
1781 self.email_address = Some(email.to_string());
1782 self
1783 }
1784
1785 #[must_use]
1787 pub fn phone(mut self, phone: &str) -> Self {
1788 self.phone_number = Some(phone.to_string());
1789 self
1790 }
1791}
1792
1793#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
1795#[serde(rename_all = "snake_case")]
1796pub enum DocumentType {
1797 IdentityVerification,
1799 AddressVerification,
1801 DateOfBirthVerification,
1803 TaxIdVerification,
1805 AccountApprovalLetter,
1807 W8ben,
1809}
1810
1811#[derive(Debug, Serialize, Deserialize, Clone)]
1813pub struct Document {
1814 pub document_type: DocumentType,
1816 #[serde(skip_serializing_if = "Option::is_none")]
1818 pub document_sub_type: Option<String>,
1819 pub content: String,
1821 pub mime_type: String,
1823}
1824
1825#[derive(Debug, Serialize, Deserialize, Clone)]
1827pub struct BrokerAccount {
1828 pub id: String,
1830 pub account_number: String,
1832 pub status: BrokerAccountStatus,
1834 #[serde(skip_serializing_if = "Option::is_none")]
1836 pub crypto_status: Option<BrokerAccountStatus>,
1837 pub currency: String,
1839 pub created_at: DateTime<Utc>,
1841 #[serde(skip_serializing_if = "Option::is_none")]
1843 pub contact: Option<Contact>,
1844 #[serde(skip_serializing_if = "Option::is_none")]
1846 pub identity: Option<Identity>,
1847 #[serde(skip_serializing_if = "Option::is_none")]
1849 pub disclosures: Option<Disclosures>,
1850 #[serde(skip_serializing_if = "Option::is_none")]
1852 pub agreements: Option<Vec<Agreement>>,
1853 #[serde(skip_serializing_if = "Option::is_none")]
1855 pub trusted_contact: Option<TrustedContact>,
1856}
1857
1858#[derive(Debug, Serialize, Deserialize, Clone)]
1860pub struct CreateBrokerAccountRequest {
1861 pub contact: Contact,
1863 pub identity: Identity,
1865 pub disclosures: Disclosures,
1867 pub agreements: Vec<Agreement>,
1869 #[serde(skip_serializing_if = "Option::is_none")]
1871 pub documents: Option<Vec<Document>>,
1872 #[serde(skip_serializing_if = "Option::is_none")]
1874 pub trusted_contact: Option<TrustedContact>,
1875 #[serde(skip_serializing_if = "Option::is_none")]
1877 pub enabled_assets: Option<Vec<String>>,
1878}
1879
1880impl CreateBrokerAccountRequest {
1881 #[must_use]
1883 pub fn new(
1884 contact: Contact,
1885 identity: Identity,
1886 disclosures: Disclosures,
1887 agreements: Vec<Agreement>,
1888 ) -> Self {
1889 Self {
1890 contact,
1891 identity,
1892 disclosures,
1893 agreements,
1894 documents: None,
1895 trusted_contact: None,
1896 enabled_assets: None,
1897 }
1898 }
1899
1900 #[must_use]
1902 pub fn documents(mut self, documents: Vec<Document>) -> Self {
1903 self.documents = Some(documents);
1904 self
1905 }
1906
1907 #[must_use]
1909 pub fn trusted_contact(mut self, contact: TrustedContact) -> Self {
1910 self.trusted_contact = Some(contact);
1911 self
1912 }
1913
1914 #[must_use]
1916 pub fn enabled_assets(mut self, assets: Vec<String>) -> Self {
1917 self.enabled_assets = Some(assets);
1918 self
1919 }
1920}
1921
1922#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1924pub struct UpdateBrokerAccountRequest {
1925 #[serde(skip_serializing_if = "Option::is_none")]
1927 pub contact: Option<Contact>,
1928 #[serde(skip_serializing_if = "Option::is_none")]
1930 pub identity: Option<Identity>,
1931 #[serde(skip_serializing_if = "Option::is_none")]
1933 pub disclosures: Option<Disclosures>,
1934 #[serde(skip_serializing_if = "Option::is_none")]
1936 pub trusted_contact: Option<TrustedContact>,
1937}
1938
1939#[derive(Debug, Serialize, Deserialize, Clone)]
1941pub struct CipInfo {
1942 pub provider_name: Vec<String>,
1944 #[serde(skip_serializing_if = "Option::is_none")]
1946 pub id: Option<String>,
1947 #[serde(skip_serializing_if = "Option::is_none")]
1949 pub result: Option<String>,
1950 #[serde(skip_serializing_if = "Option::is_none")]
1952 pub status: Option<String>,
1953 #[serde(skip_serializing_if = "Option::is_none")]
1955 pub created_at: Option<DateTime<Utc>>,
1956 #[serde(skip_serializing_if = "Option::is_none")]
1958 pub updated_at: Option<DateTime<Utc>>,
1959}
1960
1961#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1963pub struct ListBrokerAccountsParams {
1964 #[serde(skip_serializing_if = "Option::is_none")]
1966 pub query: Option<String>,
1967 #[serde(skip_serializing_if = "Option::is_none")]
1969 pub created_after: Option<String>,
1970 #[serde(skip_serializing_if = "Option::is_none")]
1972 pub created_before: Option<String>,
1973 #[serde(skip_serializing_if = "Option::is_none")]
1975 pub status: Option<BrokerAccountStatus>,
1976 #[serde(skip_serializing_if = "Option::is_none")]
1978 pub sort: Option<String>,
1979 #[serde(skip_serializing_if = "Option::is_none")]
1981 pub entities: Option<String>,
1982}
1983
1984impl ListBrokerAccountsParams {
1985 #[must_use]
1987 pub fn new() -> Self {
1988 Self::default()
1989 }
1990
1991 #[must_use]
1993 pub fn query(mut self, query: &str) -> Self {
1994 self.query = Some(query.to_string());
1995 self
1996 }
1997
1998 #[must_use]
2000 pub fn status(mut self, status: BrokerAccountStatus) -> Self {
2001 self.status = Some(status);
2002 self
2003 }
2004
2005 #[must_use]
2007 pub fn sort_desc(mut self) -> Self {
2008 self.sort = Some("desc".to_string());
2009 self
2010 }
2011}
2012
2013#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2019#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2020pub enum AchRelationshipStatus {
2021 Queued,
2023 Approved,
2025 Pending,
2027 CancelRequested,
2029 Canceled,
2031}
2032
2033#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2035#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2036pub enum BankAccountType {
2037 Checking,
2039 Savings,
2041}
2042
2043#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2045#[serde(rename_all = "lowercase")]
2046pub enum TransferType {
2047 Ach,
2049 Wire,
2051}
2052
2053#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2055#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2056pub enum TransferDirection {
2057 Incoming,
2059 Outgoing,
2061}
2062
2063#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2065#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2066pub enum TransferStatus {
2067 Queued,
2069 Pending,
2071 SentToClearing,
2073 Approved,
2075 Complete,
2077 Returned,
2079 Canceled,
2081}
2082
2083#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2085#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2086pub enum JournalEntryType {
2087 Jnlc,
2089 Jnls,
2091}
2092
2093#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2095#[serde(rename_all = "lowercase")]
2096pub enum JournalStatus {
2097 Pending,
2099 Executed,
2101 Canceled,
2103 Rejected,
2105}
2106
2107#[derive(Debug, Serialize, Deserialize, Clone)]
2109pub struct AchRelationship {
2110 pub id: String,
2112 pub account_id: String,
2114 pub status: AchRelationshipStatus,
2116 pub account_owner_name: String,
2118 pub bank_account_type: BankAccountType,
2120 pub bank_account_number: String,
2122 pub bank_routing_number: String,
2124 #[serde(skip_serializing_if = "Option::is_none")]
2126 pub nickname: Option<String>,
2127 pub created_at: DateTime<Utc>,
2129 #[serde(skip_serializing_if = "Option::is_none")]
2131 pub updated_at: Option<DateTime<Utc>>,
2132}
2133
2134#[derive(Debug, Serialize, Deserialize, Clone)]
2136pub struct CreateAchRelationshipRequest {
2137 pub account_owner_name: String,
2139 pub bank_account_type: BankAccountType,
2141 pub bank_account_number: String,
2143 pub bank_routing_number: String,
2145 #[serde(skip_serializing_if = "Option::is_none")]
2147 pub nickname: Option<String>,
2148 #[serde(skip_serializing_if = "Option::is_none")]
2150 pub processor_token: Option<String>,
2151}
2152
2153impl CreateAchRelationshipRequest {
2154 #[must_use]
2156 pub fn new(
2157 account_owner_name: &str,
2158 bank_account_type: BankAccountType,
2159 bank_account_number: &str,
2160 bank_routing_number: &str,
2161 ) -> Self {
2162 Self {
2163 account_owner_name: account_owner_name.to_string(),
2164 bank_account_type,
2165 bank_account_number: bank_account_number.to_string(),
2166 bank_routing_number: bank_routing_number.to_string(),
2167 nickname: None,
2168 processor_token: None,
2169 }
2170 }
2171
2172 #[must_use]
2174 pub fn nickname(mut self, nickname: &str) -> Self {
2175 self.nickname = Some(nickname.to_string());
2176 self
2177 }
2178
2179 #[must_use]
2181 pub fn processor_token(mut self, token: &str) -> Self {
2182 self.processor_token = Some(token.to_string());
2183 self
2184 }
2185}
2186
2187#[derive(Debug, Serialize, Deserialize, Clone)]
2189pub struct Transfer {
2190 pub id: String,
2192 #[serde(skip_serializing_if = "Option::is_none")]
2194 pub relationship_id: Option<String>,
2195 pub account_id: String,
2197 #[serde(rename = "type")]
2199 pub transfer_type: TransferType,
2200 pub status: TransferStatus,
2202 pub amount: String,
2204 pub direction: TransferDirection,
2206 pub created_at: DateTime<Utc>,
2208 #[serde(skip_serializing_if = "Option::is_none")]
2210 pub updated_at: Option<DateTime<Utc>>,
2211 #[serde(skip_serializing_if = "Option::is_none")]
2213 pub expires_at: Option<DateTime<Utc>>,
2214 #[serde(skip_serializing_if = "Option::is_none")]
2216 pub reason: Option<String>,
2217}
2218
2219#[derive(Debug, Serialize, Deserialize, Clone)]
2221pub struct CreateTransferRequest {
2222 pub transfer_type: TransferType,
2224 #[serde(skip_serializing_if = "Option::is_none")]
2226 pub relationship_id: Option<String>,
2227 pub amount: String,
2229 pub direction: TransferDirection,
2231 #[serde(skip_serializing_if = "Option::is_none")]
2233 pub additional_information: Option<String>,
2234}
2235
2236impl CreateTransferRequest {
2237 #[must_use]
2239 pub fn ach(relationship_id: &str, amount: &str, direction: TransferDirection) -> Self {
2240 Self {
2241 transfer_type: TransferType::Ach,
2242 relationship_id: Some(relationship_id.to_string()),
2243 amount: amount.to_string(),
2244 direction,
2245 additional_information: None,
2246 }
2247 }
2248
2249 #[must_use]
2251 pub fn wire(amount: &str, direction: TransferDirection) -> Self {
2252 Self {
2253 transfer_type: TransferType::Wire,
2254 relationship_id: None,
2255 amount: amount.to_string(),
2256 direction,
2257 additional_information: None,
2258 }
2259 }
2260}
2261
2262#[derive(Debug, Serialize, Deserialize, Clone)]
2264pub struct WireBank {
2265 pub id: String,
2267 pub account_id: String,
2269 pub name: String,
2271 #[serde(skip_serializing_if = "Option::is_none")]
2273 pub bank_code: Option<String>,
2274 #[serde(skip_serializing_if = "Option::is_none")]
2276 pub bank_code_type: Option<String>,
2277 #[serde(skip_serializing_if = "Option::is_none")]
2279 pub country: Option<String>,
2280 #[serde(skip_serializing_if = "Option::is_none")]
2282 pub state_province: Option<String>,
2283 #[serde(skip_serializing_if = "Option::is_none")]
2285 pub postal_code: Option<String>,
2286 #[serde(skip_serializing_if = "Option::is_none")]
2288 pub city: Option<String>,
2289 #[serde(skip_serializing_if = "Option::is_none")]
2291 pub street_address: Option<String>,
2292 #[serde(skip_serializing_if = "Option::is_none")]
2294 pub account_number: Option<String>,
2295 pub created_at: DateTime<Utc>,
2297}
2298
2299#[derive(Debug, Serialize, Deserialize, Clone)]
2301pub struct CreateWireBankRequest {
2302 pub name: String,
2304 #[serde(skip_serializing_if = "Option::is_none")]
2306 pub bank_code: Option<String>,
2307 #[serde(skip_serializing_if = "Option::is_none")]
2309 pub bank_code_type: Option<String>,
2310 #[serde(skip_serializing_if = "Option::is_none")]
2312 pub country: Option<String>,
2313 #[serde(skip_serializing_if = "Option::is_none")]
2315 pub city: Option<String>,
2316 #[serde(skip_serializing_if = "Option::is_none")]
2318 pub account_number: Option<String>,
2319}
2320
2321#[derive(Debug, Serialize, Deserialize, Clone)]
2323pub struct Journal {
2324 pub id: String,
2326 pub from_account: String,
2328 pub to_account: String,
2330 pub entry_type: JournalEntryType,
2332 pub status: JournalStatus,
2334 #[serde(skip_serializing_if = "Option::is_none")]
2336 pub net_amount: Option<String>,
2337 #[serde(skip_serializing_if = "Option::is_none")]
2339 pub symbol: Option<String>,
2340 #[serde(skip_serializing_if = "Option::is_none")]
2342 pub qty: Option<String>,
2343 #[serde(skip_serializing_if = "Option::is_none")]
2345 pub description: Option<String>,
2346 #[serde(skip_serializing_if = "Option::is_none")]
2348 pub settle_date: Option<String>,
2349 #[serde(skip_serializing_if = "Option::is_none")]
2351 pub system_date: Option<String>,
2352}
2353
2354#[derive(Debug, Serialize, Deserialize, Clone)]
2356pub struct CreateJournalRequest {
2357 pub from_account: String,
2359 pub to_account: String,
2361 pub entry_type: JournalEntryType,
2363 #[serde(skip_serializing_if = "Option::is_none")]
2365 pub amount: Option<String>,
2366 #[serde(skip_serializing_if = "Option::is_none")]
2368 pub symbol: Option<String>,
2369 #[serde(skip_serializing_if = "Option::is_none")]
2371 pub qty: Option<String>,
2372 #[serde(skip_serializing_if = "Option::is_none")]
2374 pub description: Option<String>,
2375}
2376
2377impl CreateJournalRequest {
2378 #[must_use]
2380 pub fn cash(from_account: &str, to_account: &str, amount: &str) -> Self {
2381 Self {
2382 from_account: from_account.to_string(),
2383 to_account: to_account.to_string(),
2384 entry_type: JournalEntryType::Jnlc,
2385 amount: Some(amount.to_string()),
2386 symbol: None,
2387 qty: None,
2388 description: None,
2389 }
2390 }
2391
2392 #[must_use]
2394 pub fn security(from_account: &str, to_account: &str, symbol: &str, qty: &str) -> Self {
2395 Self {
2396 from_account: from_account.to_string(),
2397 to_account: to_account.to_string(),
2398 entry_type: JournalEntryType::Jnls,
2399 amount: None,
2400 symbol: Some(symbol.to_string()),
2401 qty: Some(qty.to_string()),
2402 description: None,
2403 }
2404 }
2405
2406 #[must_use]
2408 pub fn description(mut self, description: &str) -> Self {
2409 self.description = Some(description.to_string());
2410 self
2411 }
2412}
2413
2414#[derive(Debug, Serialize, Deserialize, Clone)]
2416pub struct BatchJournalEntry {
2417 pub to_account: String,
2419 pub amount: String,
2421}
2422
2423#[derive(Debug, Serialize, Deserialize, Clone)]
2425pub struct CreateBatchJournalRequest {
2426 pub from_account: String,
2428 pub entry_type: JournalEntryType,
2430 pub entries: Vec<BatchJournalEntry>,
2432 #[serde(skip_serializing_if = "Option::is_none")]
2434 pub description: Option<String>,
2435}
2436
2437#[derive(Debug, Serialize, Deserialize, Clone, Default)]
2439pub struct ListTransfersParams {
2440 #[serde(skip_serializing_if = "Option::is_none")]
2442 pub direction: Option<TransferDirection>,
2443 #[serde(skip_serializing_if = "Option::is_none")]
2445 pub limit: Option<u32>,
2446 #[serde(skip_serializing_if = "Option::is_none")]
2448 pub offset: Option<u32>,
2449}
2450
2451#[derive(Debug, Serialize, Deserialize, Clone, Default)]
2453pub struct ListJournalsParams {
2454 #[serde(skip_serializing_if = "Option::is_none")]
2456 pub after: Option<String>,
2457 #[serde(skip_serializing_if = "Option::is_none")]
2459 pub before: Option<String>,
2460 #[serde(skip_serializing_if = "Option::is_none")]
2462 pub status: Option<JournalStatus>,
2463 #[serde(skip_serializing_if = "Option::is_none")]
2465 pub entry_type: Option<JournalEntryType>,
2466 #[serde(skip_serializing_if = "Option::is_none")]
2468 pub to_account: Option<String>,
2469 #[serde(skip_serializing_if = "Option::is_none")]
2471 pub from_account: Option<String>,
2472}
2473
2474#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2480#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2481pub enum CryptoChain {
2482 Btc,
2484 Eth,
2486 Sol,
2488 Avax,
2490 Matic,
2492 Arb,
2494 Base,
2496}
2497
2498#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2500#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2501pub enum CryptoTransferStatus {
2502 Pending,
2504 Approved,
2506 PendingSend,
2508 Sent,
2510 Complete,
2512 Rejected,
2514 Failed,
2516}
2517
2518#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2520#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2521pub enum CryptoTransferDirection {
2522 Incoming,
2524 Outgoing,
2526}
2527
2528#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2530#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
2531pub enum CryptoWalletStatus {
2532 Active,
2534 Inactive,
2536 Pending,
2538}
2539
2540#[derive(Debug, Serialize, Deserialize, Clone)]
2542pub struct BrokerCryptoWallet {
2543 pub id: String,
2545 pub account_id: String,
2547 pub asset: String,
2549 pub address: String,
2551 pub chain: CryptoChain,
2553 pub status: CryptoWalletStatus,
2555 pub created_at: DateTime<Utc>,
2557}
2558
2559#[derive(Debug, Serialize, Deserialize, Clone)]
2561pub struct CreateCryptoWalletRequest {
2562 pub asset: String,
2564}
2565
2566impl CreateCryptoWalletRequest {
2567 #[must_use]
2569 pub fn new(asset: &str) -> Self {
2570 Self {
2571 asset: asset.to_string(),
2572 }
2573 }
2574}
2575
2576#[derive(Debug, Serialize, Deserialize, Clone)]
2578pub struct CryptoTransfer {
2579 pub id: String,
2581 pub wallet_id: String,
2583 pub account_id: String,
2585 pub asset: String,
2587 pub amount: String,
2589 pub direction: CryptoTransferDirection,
2591 pub status: CryptoTransferStatus,
2593 #[serde(skip_serializing_if = "Option::is_none")]
2595 pub fee: Option<String>,
2596 #[serde(skip_serializing_if = "Option::is_none")]
2598 pub tx_hash: Option<String>,
2599 pub created_at: DateTime<Utc>,
2601 #[serde(skip_serializing_if = "Option::is_none")]
2603 pub updated_at: Option<DateTime<Utc>>,
2604}
2605
2606#[derive(Debug, Serialize, Deserialize, Clone)]
2608pub struct CreateCryptoTransferRequest {
2609 pub amount: String,
2611 #[serde(skip_serializing_if = "Option::is_none")]
2613 pub address: Option<String>,
2614}
2615
2616impl CreateCryptoTransferRequest {
2617 #[must_use]
2619 pub fn withdrawal(amount: &str, address: &str) -> Self {
2620 Self {
2621 amount: amount.to_string(),
2622 address: Some(address.to_string()),
2623 }
2624 }
2625}
2626
2627#[derive(Debug, Serialize, Deserialize, Clone)]
2629pub struct CryptoWhitelistAddress {
2630 pub id: String,
2632 pub account_id: String,
2634 pub asset: String,
2636 pub address: String,
2638 pub chain: CryptoChain,
2640 #[serde(skip_serializing_if = "Option::is_none")]
2642 pub label: Option<String>,
2643 pub created_at: DateTime<Utc>,
2645}
2646
2647#[derive(Debug, Serialize, Deserialize, Clone)]
2649pub struct CreateCryptoWhitelistRequest {
2650 pub asset: String,
2652 pub address: String,
2654 #[serde(skip_serializing_if = "Option::is_none")]
2656 pub label: Option<String>,
2657}
2658
2659impl CreateCryptoWhitelistRequest {
2660 #[must_use]
2662 pub fn new(asset: &str, address: &str) -> Self {
2663 Self {
2664 asset: asset.to_string(),
2665 address: address.to_string(),
2666 label: None,
2667 }
2668 }
2669
2670 #[must_use]
2672 pub fn label(mut self, label: &str) -> Self {
2673 self.label = Some(label.to_string());
2674 self
2675 }
2676}
2677
2678#[derive(Debug, Serialize, Deserialize, Clone)]
2680pub struct CryptoSnapshot {
2681 #[serde(rename = "latestTrade")]
2683 pub latest_trade: Option<CryptoTrade>,
2684 #[serde(rename = "latestQuote")]
2686 pub latest_quote: Option<CryptoQuote>,
2687 #[serde(rename = "minuteBar")]
2689 pub minute_bar: Option<CryptoBar>,
2690 #[serde(rename = "dailyBar")]
2692 pub daily_bar: Option<CryptoBar>,
2693 #[serde(rename = "prevDailyBar")]
2695 pub prev_daily_bar: Option<CryptoBar>,
2696}
2697
2698#[derive(Debug, Serialize, Deserialize, Clone)]
2700pub struct CryptoTrade {
2701 #[serde(rename = "t")]
2703 pub timestamp: DateTime<Utc>,
2704 #[serde(rename = "p")]
2706 pub price: f64,
2707 #[serde(rename = "s")]
2709 pub size: f64,
2710 #[serde(rename = "tks")]
2712 pub taker_side: String,
2713 #[serde(rename = "i")]
2715 pub id: u64,
2716}
2717
2718#[derive(Debug, Serialize, Deserialize, Clone)]
2720pub struct CryptoQuote {
2721 #[serde(rename = "t")]
2723 pub timestamp: DateTime<Utc>,
2724 #[serde(rename = "bp")]
2726 pub bid_price: f64,
2727 #[serde(rename = "bs")]
2729 pub bid_size: f64,
2730 #[serde(rename = "ap")]
2732 pub ask_price: f64,
2733 #[serde(rename = "as")]
2735 pub ask_size: f64,
2736}
2737
2738#[derive(Debug, Serialize, Deserialize, Clone)]
2740pub struct CryptoBar {
2741 #[serde(rename = "t")]
2743 pub timestamp: DateTime<Utc>,
2744 #[serde(rename = "o")]
2746 pub open: f64,
2747 #[serde(rename = "h")]
2749 pub high: f64,
2750 #[serde(rename = "l")]
2752 pub low: f64,
2753 #[serde(rename = "c")]
2755 pub close: f64,
2756 #[serde(rename = "v")]
2758 pub volume: f64,
2759 #[serde(rename = "n", skip_serializing_if = "Option::is_none")]
2761 pub trade_count: Option<u64>,
2762 #[serde(rename = "vw", skip_serializing_if = "Option::is_none")]
2764 pub vwap: Option<f64>,
2765}
2766
2767#[derive(Debug, Serialize, Deserialize, Clone)]
2769pub struct CryptoOrderbookEntry {
2770 #[serde(rename = "p")]
2772 pub price: f64,
2773 #[serde(rename = "s")]
2775 pub size: f64,
2776}
2777
2778#[derive(Debug, Serialize, Deserialize, Clone)]
2780pub struct CryptoOrderbook {
2781 #[serde(rename = "t")]
2783 pub timestamp: DateTime<Utc>,
2784 #[serde(rename = "b")]
2786 pub bids: Vec<CryptoOrderbookEntry>,
2787 #[serde(rename = "a")]
2789 pub asks: Vec<CryptoOrderbookEntry>,
2790}
2791
2792#[derive(Debug, Serialize, Deserialize, Clone, Default)]
2794pub struct CryptoBarsParams {
2795 #[serde(skip_serializing_if = "Option::is_none")]
2797 pub symbols: Option<String>,
2798 #[serde(skip_serializing_if = "Option::is_none")]
2800 pub timeframe: Option<String>,
2801 #[serde(skip_serializing_if = "Option::is_none")]
2803 pub start: Option<String>,
2804 #[serde(skip_serializing_if = "Option::is_none")]
2806 pub end: Option<String>,
2807 #[serde(skip_serializing_if = "Option::is_none")]
2809 pub limit: Option<u32>,
2810}
2811
2812impl CryptoBarsParams {
2813 #[must_use]
2815 pub fn new(symbols: &str) -> Self {
2816 Self {
2817 symbols: Some(symbols.to_string()),
2818 ..Default::default()
2819 }
2820 }
2821
2822 #[must_use]
2824 pub fn timeframe(mut self, timeframe: &str) -> Self {
2825 self.timeframe = Some(timeframe.to_string());
2826 self
2827 }
2828
2829 #[must_use]
2831 pub fn time_range(mut self, start: &str, end: &str) -> Self {
2832 self.start = Some(start.to_string());
2833 self.end = Some(end.to_string());
2834 self
2835 }
2836
2837 #[must_use]
2839 pub fn limit(mut self, limit: u32) -> Self {
2840 self.limit = Some(limit);
2841 self
2842 }
2843}
2844
2845#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2851#[serde(rename_all = "lowercase")]
2852pub enum NewsContentType {
2853 Article,
2855 Video,
2857 Audio,
2859}
2860
2861#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2863#[serde(rename_all = "lowercase")]
2864pub enum NewsImageSize {
2865 Thumb,
2867 Small,
2869 Large,
2871}
2872
2873#[derive(Debug, Serialize, Deserialize, Clone)]
2875pub struct NewsImage {
2876 pub size: NewsImageSize,
2878 pub url: String,
2880}
2881
2882#[derive(Debug, Serialize, Deserialize, Clone)]
2884pub struct NewsSource {
2885 pub name: String,
2887 #[serde(skip_serializing_if = "Option::is_none")]
2889 pub url: Option<String>,
2890 #[serde(skip_serializing_if = "Option::is_none")]
2892 pub favicon_url: Option<String>,
2893}
2894
2895#[derive(Debug, Serialize, Deserialize, Clone)]
2897pub struct EnhancedNewsArticle {
2898 pub id: u64,
2900 pub headline: String,
2902 #[serde(skip_serializing_if = "Option::is_none")]
2904 pub author: Option<String>,
2905 pub created_at: DateTime<Utc>,
2907 #[serde(skip_serializing_if = "Option::is_none")]
2909 pub updated_at: Option<DateTime<Utc>>,
2910 #[serde(skip_serializing_if = "Option::is_none")]
2912 pub summary: Option<String>,
2913 #[serde(skip_serializing_if = "Option::is_none")]
2915 pub content: Option<String>,
2916 #[serde(skip_serializing_if = "Option::is_none")]
2918 pub url: Option<String>,
2919 #[serde(default)]
2921 pub images: Vec<NewsImage>,
2922 #[serde(default)]
2924 pub symbols: Vec<String>,
2925 #[serde(skip_serializing_if = "Option::is_none")]
2927 pub source: Option<String>,
2928}
2929
2930#[derive(Debug, Serialize, Deserialize, Clone, Default)]
2932pub struct NewsParams {
2933 #[serde(skip_serializing_if = "Option::is_none")]
2935 pub symbols: Option<String>,
2936 #[serde(skip_serializing_if = "Option::is_none")]
2938 pub start: Option<String>,
2939 #[serde(skip_serializing_if = "Option::is_none")]
2941 pub end: Option<String>,
2942 #[serde(skip_serializing_if = "Option::is_none")]
2944 pub sort: Option<String>,
2945 #[serde(skip_serializing_if = "Option::is_none")]
2947 pub include_content: Option<bool>,
2948 #[serde(skip_serializing_if = "Option::is_none")]
2950 pub exclude_contentless: Option<bool>,
2951 #[serde(skip_serializing_if = "Option::is_none")]
2953 pub limit: Option<u32>,
2954 #[serde(skip_serializing_if = "Option::is_none")]
2956 pub page_token: Option<String>,
2957}
2958
2959impl NewsParams {
2960 #[must_use]
2962 pub fn new() -> Self {
2963 Self::default()
2964 }
2965
2966 #[must_use]
2968 pub fn symbols(mut self, symbols: &str) -> Self {
2969 self.symbols = Some(symbols.to_string());
2970 self
2971 }
2972
2973 #[must_use]
2975 pub fn time_range(mut self, start: &str, end: &str) -> Self {
2976 self.start = Some(start.to_string());
2977 self.end = Some(end.to_string());
2978 self
2979 }
2980
2981 #[must_use]
2983 pub fn sort_desc(mut self) -> Self {
2984 self.sort = Some("desc".to_string());
2985 self
2986 }
2987
2988 #[must_use]
2990 pub fn sort_asc(mut self) -> Self {
2991 self.sort = Some("asc".to_string());
2992 self
2993 }
2994
2995 #[must_use]
2997 pub fn with_content(mut self) -> Self {
2998 self.include_content = Some(true);
2999 self
3000 }
3001
3002 #[must_use]
3004 pub fn exclude_empty(mut self) -> Self {
3005 self.exclude_contentless = Some(true);
3006 self
3007 }
3008
3009 #[must_use]
3011 pub fn limit(mut self, limit: u32) -> Self {
3012 self.limit = Some(limit);
3013 self
3014 }
3015
3016 #[must_use]
3018 pub fn page_token(mut self, token: &str) -> Self {
3019 self.page_token = Some(token.to_string());
3020 self
3021 }
3022}
3023
3024#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3030#[serde(rename_all = "snake_case")]
3031pub enum OAuthScope {
3032 #[serde(rename = "account:write")]
3034 AccountWrite,
3035 Trading,
3037 Data,
3039}
3040
3041impl std::fmt::Display for OAuthScope {
3042 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3043 match self {
3044 OAuthScope::AccountWrite => write!(f, "account:write"),
3045 OAuthScope::Trading => write!(f, "trading"),
3046 OAuthScope::Data => write!(f, "data"),
3047 }
3048 }
3049}
3050
3051#[derive(Debug, Clone)]
3053pub struct OAuthConfig {
3054 pub client_id: String,
3056 pub client_secret: String,
3058 pub redirect_uri: String,
3060 pub scopes: Vec<OAuthScope>,
3062}
3063
3064impl OAuthConfig {
3065 #[must_use]
3067 pub fn new(client_id: &str, client_secret: &str, redirect_uri: &str) -> Self {
3068 Self {
3069 client_id: client_id.to_string(),
3070 client_secret: client_secret.to_string(),
3071 redirect_uri: redirect_uri.to_string(),
3072 scopes: vec![],
3073 }
3074 }
3075
3076 #[must_use]
3078 pub fn scope(mut self, scope: OAuthScope) -> Self {
3079 self.scopes.push(scope);
3080 self
3081 }
3082
3083 #[must_use]
3085 pub fn scopes(mut self, scopes: Vec<OAuthScope>) -> Self {
3086 self.scopes.extend(scopes);
3087 self
3088 }
3089
3090 #[must_use]
3092 pub fn authorization_url(&self, state: &str) -> String {
3093 let scopes_str: String = self
3094 .scopes
3095 .iter()
3096 .map(|s| s.to_string())
3097 .collect::<Vec<_>>()
3098 .join(" ");
3099
3100 format!(
3101 "https://app.alpaca.markets/oauth/authorize?response_type=code&client_id={}&redirect_uri={}&state={}&scope={}",
3102 urlencoding::encode(&self.client_id),
3103 urlencoding::encode(&self.redirect_uri),
3104 urlencoding::encode(state),
3105 urlencoding::encode(&scopes_str)
3106 )
3107 }
3108}
3109
3110#[derive(Debug, Serialize, Deserialize, Clone)]
3112pub struct OAuthTokenRequest {
3113 pub grant_type: String,
3115 #[serde(skip_serializing_if = "Option::is_none")]
3117 pub code: Option<String>,
3118 pub client_id: String,
3120 pub client_secret: String,
3122 #[serde(skip_serializing_if = "Option::is_none")]
3124 pub redirect_uri: Option<String>,
3125 #[serde(skip_serializing_if = "Option::is_none")]
3127 pub refresh_token: Option<String>,
3128}
3129
3130impl OAuthTokenRequest {
3131 #[must_use]
3133 pub fn authorization_code(config: &OAuthConfig, code: &str) -> Self {
3134 Self {
3135 grant_type: "authorization_code".to_string(),
3136 code: Some(code.to_string()),
3137 client_id: config.client_id.clone(),
3138 client_secret: config.client_secret.clone(),
3139 redirect_uri: Some(config.redirect_uri.clone()),
3140 refresh_token: None,
3141 }
3142 }
3143
3144 #[must_use]
3146 pub fn refresh(config: &OAuthConfig, refresh_token: &str) -> Self {
3147 Self {
3148 grant_type: "refresh_token".to_string(),
3149 code: None,
3150 client_id: config.client_id.clone(),
3151 client_secret: config.client_secret.clone(),
3152 redirect_uri: None,
3153 refresh_token: Some(refresh_token.to_string()),
3154 }
3155 }
3156}
3157
3158#[derive(Debug, Serialize, Deserialize, Clone)]
3160pub struct OAuthRevokeRequest {
3161 pub token: String,
3163 pub client_id: String,
3165 pub client_secret: String,
3167}
3168
3169impl OAuthRevokeRequest {
3170 #[must_use]
3172 pub fn new(config: &OAuthConfig, token: &str) -> Self {
3173 Self {
3174 token: token.to_string(),
3175 client_id: config.client_id.clone(),
3176 client_secret: config.client_secret.clone(),
3177 }
3178 }
3179}
3180
3181#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3187#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3188pub enum AccountStatusEventType {
3189 AccountOpened,
3191 AccountUpdated,
3193 AccountApprovalPending,
3195 AccountApproved,
3197 AccountRejected,
3199 AccountDisabled,
3201 AccountEnabled,
3203}
3204
3205#[derive(Debug, Serialize, Deserialize, Clone)]
3207pub struct AccountStatusEvent {
3208 pub id: String,
3210 pub account_id: String,
3212 pub event_type: AccountStatusEventType,
3214 pub at: DateTime<Utc>,
3216 #[serde(skip_serializing_if = "Option::is_none")]
3218 pub status_message: Option<String>,
3219}
3220
3221#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3223#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3224pub enum TransferStatusEventType {
3225 TransferQueued,
3227 TransferPending,
3229 TransferApproved,
3231 TransferComplete,
3233 TransferReturned,
3235 TransferCanceled,
3237}
3238
3239#[derive(Debug, Serialize, Deserialize, Clone)]
3241pub struct TransferStatusEvent {
3242 pub id: String,
3244 pub account_id: String,
3246 pub transfer_id: String,
3248 pub event_type: TransferStatusEventType,
3250 pub at: DateTime<Utc>,
3252 #[serde(skip_serializing_if = "Option::is_none")]
3254 pub amount: Option<String>,
3255}
3256
3257#[derive(Debug, Serialize, Deserialize, Clone)]
3259pub struct BrokerTradeEvent {
3260 pub id: String,
3262 pub account_id: String,
3264 pub order_id: String,
3266 pub symbol: String,
3268 pub side: OrderSide,
3270 pub qty: String,
3272 pub price: String,
3274 pub at: DateTime<Utc>,
3276}
3277
3278#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3280#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3281pub enum JournalStatusEventType {
3282 JournalPending,
3284 JournalExecuted,
3286 JournalCanceled,
3288 JournalRejected,
3290}
3291
3292#[derive(Debug, Serialize, Deserialize, Clone)]
3294pub struct JournalStatusEvent {
3295 pub id: String,
3297 pub journal_id: String,
3299 pub event_type: JournalStatusEventType,
3301 pub at: DateTime<Utc>,
3303 #[serde(skip_serializing_if = "Option::is_none")]
3305 pub from_account: Option<String>,
3306 #[serde(skip_serializing_if = "Option::is_none")]
3308 pub to_account: Option<String>,
3309}
3310
3311#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3313#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3314pub enum NonTradeActivityType {
3315 Div,
3317 Divtax,
3319 Int,
3321 Jnl,
3323 Ma,
3325 Fee,
3327 Csd,
3329 Csw,
3331}
3332
3333#[derive(Debug, Serialize, Deserialize, Clone)]
3335pub struct NonTradeActivityEvent {
3336 pub id: String,
3338 pub account_id: String,
3340 pub activity_type: NonTradeActivityType,
3342 pub at: DateTime<Utc>,
3344 #[serde(skip_serializing_if = "Option::is_none")]
3346 pub symbol: Option<String>,
3347 #[serde(skip_serializing_if = "Option::is_none")]
3349 pub amount: Option<String>,
3350 #[serde(skip_serializing_if = "Option::is_none")]
3352 pub description: Option<String>,
3353}
3354
3355#[derive(Debug, Serialize, Deserialize, Clone)]
3357#[serde(tag = "event_type")]
3358pub enum BrokerSseEvent {
3359 #[serde(rename = "account_status")]
3361 AccountStatus(AccountStatusEvent),
3362 #[serde(rename = "transfer_status")]
3364 TransferStatus(TransferStatusEvent),
3365 #[serde(rename = "trade")]
3367 Trade(BrokerTradeEvent),
3368 #[serde(rename = "journal_status")]
3370 JournalStatus(JournalStatusEvent),
3371 #[serde(rename = "nta")]
3373 NonTradeActivity(NonTradeActivityEvent),
3374}
3375
3376#[derive(Debug, Serialize, Deserialize, Clone, Default)]
3378pub struct SseEventParams {
3379 #[serde(skip_serializing_if = "Option::is_none")]
3381 pub account_id: Option<String>,
3382 #[serde(skip_serializing_if = "Option::is_none")]
3384 pub since: Option<String>,
3385 #[serde(skip_serializing_if = "Option::is_none")]
3387 pub until: Option<String>,
3388}
3389
3390impl SseEventParams {
3391 #[must_use]
3393 pub fn new() -> Self {
3394 Self::default()
3395 }
3396
3397 #[must_use]
3399 pub fn account_id(mut self, account_id: &str) -> Self {
3400 self.account_id = Some(account_id.to_string());
3401 self
3402 }
3403
3404 #[must_use]
3406 pub fn since(mut self, since: &str) -> Self {
3407 self.since = Some(since.to_string());
3408 self
3409 }
3410
3411 #[must_use]
3413 pub fn until(mut self, until: &str) -> Self {
3414 self.until = Some(until.to_string());
3415 self
3416 }
3417}
3418
3419#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3425#[serde(rename_all = "snake_case")]
3426pub enum AssetAttribute {
3427 PtpNoException,
3429 PtpWithException,
3431 Ipo,
3433 OptionsEnabled,
3435 FractionalEhEnabled,
3437}
3438
3439#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3441#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3442pub enum AssetExchange {
3443 Amex,
3445 Arca,
3447 Bats,
3449 Nyse,
3451 Nasdaq,
3453 NasdaqGm,
3455 NasdaqGs,
3457 NasdaqCm,
3459 Nysearca,
3461 Otc,
3463 Crypto,
3465 #[serde(rename = "OPRA")]
3467 Opra,
3468}
3469
3470#[derive(Debug, Serialize, Deserialize, Clone)]
3472pub struct EnhancedAsset {
3473 pub id: Uuid,
3475 pub class: String,
3477 pub exchange: AssetExchange,
3479 pub symbol: String,
3481 #[serde(skip_serializing_if = "Option::is_none")]
3483 pub name: Option<String>,
3484 pub status: AssetStatus,
3486 pub tradable: bool,
3488 pub marginable: bool,
3490 pub shortable: bool,
3492 pub easy_to_borrow: bool,
3494 pub fractionable: bool,
3496 #[serde(skip_serializing_if = "Option::is_none")]
3498 pub maintenance_margin_requirement: Option<f64>,
3499 #[serde(skip_serializing_if = "Option::is_none")]
3501 pub min_order_size: Option<String>,
3502 #[serde(skip_serializing_if = "Option::is_none")]
3504 pub min_trade_increment: Option<String>,
3505 #[serde(skip_serializing_if = "Option::is_none")]
3507 pub price_increment: Option<String>,
3508 #[serde(default)]
3510 pub attributes: Vec<AssetAttribute>,
3511}
3512
3513#[derive(Debug, Serialize, Deserialize, Clone, Default)]
3515pub struct ListAssetsParams {
3516 #[serde(skip_serializing_if = "Option::is_none")]
3518 pub status: Option<AssetStatus>,
3519 #[serde(skip_serializing_if = "Option::is_none")]
3521 pub asset_class: Option<String>,
3522 #[serde(skip_serializing_if = "Option::is_none")]
3524 pub exchange: Option<String>,
3525 #[serde(skip_serializing_if = "Option::is_none")]
3527 pub attributes: Option<String>,
3528}
3529
3530impl ListAssetsParams {
3531 #[must_use]
3533 pub fn new() -> Self {
3534 Self::default()
3535 }
3536
3537 #[must_use]
3539 pub fn status(mut self, status: AssetStatus) -> Self {
3540 self.status = Some(status);
3541 self
3542 }
3543
3544 #[must_use]
3546 pub fn asset_class(mut self, asset_class: &str) -> Self {
3547 self.asset_class = Some(asset_class.to_string());
3548 self
3549 }
3550
3551 #[must_use]
3553 pub fn exchange(mut self, exchange: &str) -> Self {
3554 self.exchange = Some(exchange.to_string());
3555 self
3556 }
3557
3558 #[must_use]
3560 pub fn attributes(mut self, attributes: &str) -> Self {
3561 self.attributes = Some(attributes.to_string());
3562 self
3563 }
3564}
3565
3566#[derive(Debug, Serialize, Deserialize, Clone)]
3568pub struct OptionContractAsset {
3569 pub id: Uuid,
3571 pub symbol: String,
3573 #[serde(skip_serializing_if = "Option::is_none")]
3575 pub name: Option<String>,
3576 pub status: AssetStatus,
3578 pub tradable: bool,
3580 pub expiration_date: String,
3582 pub strike_price: String,
3584 pub option_type: OptionType,
3586 #[serde(skip_serializing_if = "Option::is_none")]
3588 pub option_style: Option<OptionStyle>,
3589 pub underlying_symbol: String,
3591 #[serde(skip_serializing_if = "Option::is_none")]
3593 pub underlying_asset_id: Option<Uuid>,
3594 #[serde(skip_serializing_if = "Option::is_none")]
3596 pub root_symbol: Option<String>,
3597}
3598
3599#[derive(Debug, Serialize, Deserialize, Clone)]
3601pub struct CorporateActionAnnouncement {
3602 pub id: String,
3604 pub ca_type: String,
3606 #[serde(skip_serializing_if = "Option::is_none")]
3608 pub ca_sub_type: Option<String>,
3609 pub initiating_symbol: String,
3611 #[serde(skip_serializing_if = "Option::is_none")]
3613 pub initiating_original_cusip: Option<String>,
3614 #[serde(skip_serializing_if = "Option::is_none")]
3616 pub target_symbol: Option<String>,
3617 #[serde(skip_serializing_if = "Option::is_none")]
3619 pub target_original_cusip: Option<String>,
3620 #[serde(skip_serializing_if = "Option::is_none")]
3622 pub declaration_date: Option<String>,
3623 #[serde(skip_serializing_if = "Option::is_none")]
3625 pub ex_date: Option<String>,
3626 #[serde(skip_serializing_if = "Option::is_none")]
3628 pub record_date: Option<String>,
3629 #[serde(skip_serializing_if = "Option::is_none")]
3631 pub payable_date: Option<String>,
3632 #[serde(skip_serializing_if = "Option::is_none")]
3634 pub cash: Option<String>,
3635 #[serde(skip_serializing_if = "Option::is_none")]
3637 pub old_rate: Option<String>,
3638 #[serde(skip_serializing_if = "Option::is_none")]
3640 pub new_rate: Option<String>,
3641}
3642
3643#[derive(Debug, Serialize, Deserialize, Clone, Default)]
3645pub struct ListAnnouncementsParams {
3646 #[serde(skip_serializing_if = "Option::is_none")]
3648 pub ca_types: Option<String>,
3649 #[serde(skip_serializing_if = "Option::is_none")]
3651 pub since: Option<String>,
3652 #[serde(skip_serializing_if = "Option::is_none")]
3654 pub until: Option<String>,
3655 #[serde(skip_serializing_if = "Option::is_none")]
3657 pub symbol: Option<String>,
3658 #[serde(skip_serializing_if = "Option::is_none")]
3660 pub cusip: Option<String>,
3661 #[serde(skip_serializing_if = "Option::is_none")]
3663 pub date_type: Option<String>,
3664}
3665
3666impl ListAnnouncementsParams {
3667 #[must_use]
3669 pub fn new() -> Self {
3670 Self::default()
3671 }
3672
3673 #[must_use]
3675 pub fn ca_types(mut self, ca_types: &str) -> Self {
3676 self.ca_types = Some(ca_types.to_string());
3677 self
3678 }
3679
3680 #[must_use]
3682 pub fn date_range(mut self, since: &str, until: &str) -> Self {
3683 self.since = Some(since.to_string());
3684 self.until = Some(until.to_string());
3685 self
3686 }
3687
3688 #[must_use]
3690 pub fn symbol(mut self, symbol: &str) -> Self {
3691 self.symbol = Some(symbol.to_string());
3692 self
3693 }
3694
3695 #[must_use]
3697 pub fn cusip(mut self, cusip: &str) -> Self {
3698 self.cusip = Some(cusip.to_string());
3699 self
3700 }
3701}
3702
3703#[derive(Debug, Serialize, Deserialize, Clone)]
3709pub struct TradeActivity {
3710 pub id: String,
3712 pub activity_type: ActivityType,
3714 pub transaction_time: DateTime<Utc>,
3716 pub symbol: String,
3718 pub order_id: Uuid,
3720 pub side: OrderSide,
3722 pub qty: String,
3724 pub price: String,
3726 #[serde(skip_serializing_if = "Option::is_none")]
3728 pub cum_qty: Option<String>,
3729 #[serde(skip_serializing_if = "Option::is_none")]
3731 pub leaves_qty: Option<String>,
3732}
3733
3734#[derive(Debug, Serialize, Deserialize, Clone)]
3736pub struct NonTradeActivity {
3737 pub id: String,
3739 pub activity_type: ActivityType,
3741 pub date: String,
3743 pub net_amount: String,
3745 #[serde(skip_serializing_if = "Option::is_none")]
3747 pub symbol: Option<String>,
3748 #[serde(skip_serializing_if = "Option::is_none")]
3750 pub qty: Option<String>,
3751 #[serde(skip_serializing_if = "Option::is_none")]
3753 pub per_share_amount: Option<String>,
3754 #[serde(skip_serializing_if = "Option::is_none")]
3756 pub description: Option<String>,
3757}
3758
3759#[derive(Debug, Serialize, Deserialize, Clone, Default)]
3761pub struct ListActivitiesParams {
3762 #[serde(skip_serializing_if = "Option::is_none")]
3764 pub activity_types: Option<String>,
3765 #[serde(skip_serializing_if = "Option::is_none")]
3767 pub date: Option<String>,
3768 #[serde(skip_serializing_if = "Option::is_none")]
3770 pub until: Option<String>,
3771 #[serde(skip_serializing_if = "Option::is_none")]
3773 pub after: Option<String>,
3774 #[serde(skip_serializing_if = "Option::is_none")]
3776 pub direction: Option<SortDirection>,
3777 #[serde(skip_serializing_if = "Option::is_none")]
3779 pub page_size: Option<u32>,
3780 #[serde(skip_serializing_if = "Option::is_none")]
3782 pub page_token: Option<String>,
3783}
3784
3785impl ListActivitiesParams {
3786 #[must_use]
3788 pub fn new() -> Self {
3789 Self::default()
3790 }
3791
3792 #[must_use]
3794 pub fn activity_types(mut self, types: &str) -> Self {
3795 self.activity_types = Some(types.to_string());
3796 self
3797 }
3798
3799 #[must_use]
3801 pub fn date(mut self, date: &str) -> Self {
3802 self.date = Some(date.to_string());
3803 self
3804 }
3805
3806 #[must_use]
3808 pub fn until(mut self, until: &str) -> Self {
3809 self.until = Some(until.to_string());
3810 self
3811 }
3812
3813 #[must_use]
3815 pub fn after(mut self, after: &str) -> Self {
3816 self.after = Some(after.to_string());
3817 self
3818 }
3819
3820 #[must_use]
3822 pub fn direction(mut self, direction: SortDirection) -> Self {
3823 self.direction = Some(direction);
3824 self
3825 }
3826
3827 #[must_use]
3829 pub fn page_size(mut self, size: u32) -> Self {
3830 self.page_size = Some(size);
3831 self
3832 }
3833
3834 #[must_use]
3836 pub fn page_token(mut self, token: &str) -> Self {
3837 self.page_token = Some(token.to_string());
3838 self
3839 }
3840}
3841
3842#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3848pub enum PortfolioTimeframe {
3849 #[serde(rename = "1Min")]
3851 OneMin,
3852 #[serde(rename = "5Min")]
3854 FiveMin,
3855 #[serde(rename = "15Min")]
3857 FifteenMin,
3858 #[serde(rename = "1H")]
3860 OneHour,
3861 #[serde(rename = "1D")]
3863 OneDay,
3864}
3865
3866#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3868pub enum PortfolioPeriod {
3869 #[serde(rename = "1D")]
3871 OneDay,
3872 #[serde(rename = "1W")]
3874 OneWeek,
3875 #[serde(rename = "1M")]
3877 OneMonth,
3878 #[serde(rename = "3M")]
3880 ThreeMonths,
3881 #[serde(rename = "1A")]
3883 OneYear,
3884 #[serde(rename = "all")]
3886 All,
3887}
3888
3889#[derive(Debug, Serialize, Deserialize, Clone, Default)]
3891pub struct PortfolioHistoryParams {
3892 #[serde(skip_serializing_if = "Option::is_none")]
3894 pub period: Option<String>,
3895 #[serde(skip_serializing_if = "Option::is_none")]
3897 pub timeframe: Option<String>,
3898 #[serde(skip_serializing_if = "Option::is_none")]
3900 pub date_end: Option<String>,
3901 #[serde(skip_serializing_if = "Option::is_none")]
3903 pub extended_hours: Option<bool>,
3904 #[serde(skip_serializing_if = "Option::is_none")]
3906 pub intraday_reporting: Option<String>,
3907 #[serde(skip_serializing_if = "Option::is_none")]
3909 pub pnl_reset: Option<String>,
3910}
3911
3912impl PortfolioHistoryParams {
3913 #[must_use]
3915 pub fn new() -> Self {
3916 Self::default()
3917 }
3918
3919 #[must_use]
3921 pub fn period(mut self, period: PortfolioPeriod) -> Self {
3922 self.period = Some(match period {
3923 PortfolioPeriod::OneDay => "1D".to_string(),
3924 PortfolioPeriod::OneWeek => "1W".to_string(),
3925 PortfolioPeriod::OneMonth => "1M".to_string(),
3926 PortfolioPeriod::ThreeMonths => "3M".to_string(),
3927 PortfolioPeriod::OneYear => "1A".to_string(),
3928 PortfolioPeriod::All => "all".to_string(),
3929 });
3930 self
3931 }
3932
3933 #[must_use]
3935 pub fn timeframe(mut self, timeframe: PortfolioTimeframe) -> Self {
3936 self.timeframe = Some(match timeframe {
3937 PortfolioTimeframe::OneMin => "1Min".to_string(),
3938 PortfolioTimeframe::FiveMin => "5Min".to_string(),
3939 PortfolioTimeframe::FifteenMin => "15Min".to_string(),
3940 PortfolioTimeframe::OneHour => "1H".to_string(),
3941 PortfolioTimeframe::OneDay => "1D".to_string(),
3942 });
3943 self
3944 }
3945
3946 #[must_use]
3948 pub fn date_end(mut self, date_end: &str) -> Self {
3949 self.date_end = Some(date_end.to_string());
3950 self
3951 }
3952
3953 #[must_use]
3955 pub fn extended_hours(mut self, extended_hours: bool) -> Self {
3956 self.extended_hours = Some(extended_hours);
3957 self
3958 }
3959}
3960
3961#[derive(Debug, Serialize, Deserialize, Clone)]
3963pub struct TargetAllocation {
3964 pub symbol: String,
3966 #[serde(skip_serializing_if = "Option::is_none")]
3968 pub percent: Option<f64>,
3969 #[serde(skip_serializing_if = "Option::is_none")]
3971 pub notional: Option<String>,
3972}
3973
3974impl TargetAllocation {
3975 #[must_use]
3977 pub fn percent(symbol: &str, percent: f64) -> Self {
3978 Self {
3979 symbol: symbol.to_string(),
3980 percent: Some(percent),
3981 notional: None,
3982 }
3983 }
3984
3985 #[must_use]
3987 pub fn notional(symbol: &str, notional: &str) -> Self {
3988 Self {
3989 symbol: symbol.to_string(),
3990 percent: None,
3991 notional: Some(notional.to_string()),
3992 }
3993 }
3994}
3995
3996#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3998#[serde(rename_all = "lowercase")]
3999pub enum RebalanceStatus {
4000 Pending,
4002 InProgress,
4004 Completed,
4006 Failed,
4008 Canceled,
4010}
4011
4012#[derive(Debug, Serialize, Deserialize, Clone)]
4014pub struct RebalancePortfolioRequest {
4015 pub name: String,
4017 #[serde(skip_serializing_if = "Option::is_none")]
4019 pub description: Option<String>,
4020 pub weights: Vec<TargetAllocation>,
4022}
4023
4024impl RebalancePortfolioRequest {
4025 #[must_use]
4027 pub fn new(name: &str, weights: Vec<TargetAllocation>) -> Self {
4028 Self {
4029 name: name.to_string(),
4030 description: None,
4031 weights,
4032 }
4033 }
4034
4035 #[must_use]
4037 pub fn description(mut self, description: &str) -> Self {
4038 self.description = Some(description.to_string());
4039 self
4040 }
4041}
4042
4043#[derive(Debug, Serialize, Deserialize, Clone)]
4045pub struct RebalancePortfolio {
4046 pub id: Uuid,
4048 pub name: String,
4050 #[serde(skip_serializing_if = "Option::is_none")]
4052 pub description: Option<String>,
4053 pub weights: Vec<TargetAllocation>,
4055 pub created_at: DateTime<Utc>,
4057 pub updated_at: DateTime<Utc>,
4059}
4060
4061#[derive(Debug, Serialize, Deserialize, Clone)]
4063pub struct RebalanceRunRequest {
4064 pub account_id: String,
4066 pub portfolio_id: String,
4068 #[serde(rename = "type")]
4070 #[serde(skip_serializing_if = "Option::is_none")]
4071 pub run_type: Option<String>,
4072}
4073
4074impl RebalanceRunRequest {
4075 #[must_use]
4077 pub fn new(account_id: &str, portfolio_id: &str) -> Self {
4078 Self {
4079 account_id: account_id.to_string(),
4080 portfolio_id: portfolio_id.to_string(),
4081 run_type: None,
4082 }
4083 }
4084}
4085
4086#[derive(Debug, Serialize, Deserialize, Clone)]
4088pub struct RebalanceRun {
4089 pub id: Uuid,
4091 pub account_id: String,
4093 pub portfolio_id: String,
4095 pub status: RebalanceStatus,
4097 pub created_at: DateTime<Utc>,
4099 #[serde(skip_serializing_if = "Option::is_none")]
4101 pub completed_at: Option<DateTime<Utc>>,
4102}
4103
4104#[derive(Debug, Serialize, Deserialize, Clone, Default)]
4106pub struct ClosePositionParams {
4107 #[serde(skip_serializing_if = "Option::is_none")]
4109 pub qty: Option<String>,
4110 #[serde(skip_serializing_if = "Option::is_none")]
4112 pub percentage: Option<String>,
4113}
4114
4115impl ClosePositionParams {
4116 #[must_use]
4118 pub fn new() -> Self {
4119 Self::default()
4120 }
4121
4122 #[must_use]
4124 pub fn qty(mut self, qty: &str) -> Self {
4125 self.qty = Some(qty.to_string());
4126 self
4127 }
4128
4129 #[must_use]
4131 pub fn percentage(mut self, percentage: &str) -> Self {
4132 self.percentage = Some(percentage.to_string());
4133 self
4134 }
4135}
4136
4137#[derive(Debug, Clone)]
4143pub struct RateLimitConfig {
4144 pub requests_per_minute: u32,
4146 pub burst_limit: u32,
4148 pub retry_on_rate_limit: bool,
4150 pub max_retries: u32,
4152 pub base_delay_ms: u64,
4154}
4155
4156impl Default for RateLimitConfig {
4157 fn default() -> Self {
4158 Self {
4159 requests_per_minute: 200,
4160 burst_limit: 50,
4161 retry_on_rate_limit: true,
4162 max_retries: 3,
4163 base_delay_ms: 1000,
4164 }
4165 }
4166}
4167
4168impl RateLimitConfig {
4169 #[must_use]
4171 pub fn new() -> Self {
4172 Self::default()
4173 }
4174
4175 #[must_use]
4177 pub fn requests_per_minute(mut self, rpm: u32) -> Self {
4178 self.requests_per_minute = rpm;
4179 self
4180 }
4181
4182 #[must_use]
4184 pub fn burst_limit(mut self, limit: u32) -> Self {
4185 self.burst_limit = limit;
4186 self
4187 }
4188
4189 #[must_use]
4191 pub fn retry_on_rate_limit(mut self, retry: bool) -> Self {
4192 self.retry_on_rate_limit = retry;
4193 self
4194 }
4195
4196 #[must_use]
4198 pub fn max_retries(mut self, retries: u32) -> Self {
4199 self.max_retries = retries;
4200 self
4201 }
4202
4203 #[must_use]
4205 pub fn base_delay_ms(mut self, delay: u64) -> Self {
4206 self.base_delay_ms = delay;
4207 self
4208 }
4209}
4210
4211#[derive(Debug, Clone)]
4213pub struct RateLimitStatus {
4214 pub remaining: u32,
4216 pub limit: u32,
4218 pub reset_at: u64,
4220}
4221
4222impl RateLimitStatus {
4223 #[must_use]
4225 pub fn new(remaining: u32, limit: u32, reset_at: u64) -> Self {
4226 Self {
4227 remaining,
4228 limit,
4229 reset_at,
4230 }
4231 }
4232
4233 #[must_use]
4235 pub fn is_rate_limited(&self) -> bool {
4236 self.remaining == 0
4237 }
4238
4239 #[must_use]
4241 pub fn seconds_until_reset(&self) -> u64 {
4242 let now = std::time::SystemTime::now()
4243 .duration_since(std::time::UNIX_EPOCH)
4244 .unwrap_or_default()
4245 .as_secs();
4246 self.reset_at.saturating_sub(now)
4247 }
4248}
4249
4250#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
4252pub enum RequestPriority {
4253 Low = 0,
4255 #[default]
4257 Normal = 1,
4258 High = 2,
4260 Critical = 3,
4262}
4263
4264#[derive(Debug, Serialize, Deserialize, Clone)]
4270pub struct MarginInfo {
4271 pub buying_power: String,
4273 pub regt_buying_power: String,
4275 pub daytrading_buying_power: String,
4277 pub non_marginable_buying_power: String,
4279 pub initial_margin: String,
4281 pub maintenance_margin: String,
4283 pub last_maintenance_margin: String,
4285 #[serde(skip_serializing_if = "Option::is_none")]
4287 pub sma: Option<String>,
4288}
4289
4290#[derive(Debug, Serialize, Deserialize, Clone)]
4292pub struct ShortPosition {
4293 pub symbol: String,
4295 pub qty: String,
4297 pub avg_entry_price: String,
4299 pub market_value: String,
4301 pub cost_basis: String,
4303 pub unrealized_pl: String,
4305 pub unrealized_plpc: String,
4307 pub current_price: String,
4309}
4310
4311#[derive(Debug, Serialize, Deserialize, Clone)]
4313pub struct BorrowRate {
4314 pub symbol: String,
4316 pub rate: f64,
4318 pub available_qty: String,
4320 pub easy_to_borrow: bool,
4322}
4323
4324#[derive(Debug, Serialize, Deserialize, Clone)]
4326pub struct MarginRequirement {
4327 pub initial: f64,
4329 pub maintenance: f64,
4331}
4332
4333impl MarginRequirement {
4334 #[must_use]
4336 pub fn new(initial: f64, maintenance: f64) -> Self {
4337 Self {
4338 initial,
4339 maintenance,
4340 }
4341 }
4342
4343 #[must_use]
4345 pub fn standard() -> Self {
4346 Self::new(0.50, 0.25)
4347 }
4348
4349 #[must_use]
4351 pub fn calculate_initial_margin(&self, position_value: f64) -> f64 {
4352 position_value * self.initial
4353 }
4354
4355 #[must_use]
4357 pub fn calculate_maintenance_margin(&self, position_value: f64) -> f64 {
4358 position_value * self.maintenance
4359 }
4360}
4361
4362#[derive(Debug, Serialize, Deserialize, Clone)]
4364pub struct LocateRequest {
4365 pub symbol: String,
4367 pub qty: String,
4369}
4370
4371impl LocateRequest {
4372 #[must_use]
4374 pub fn new(symbol: &str, qty: &str) -> Self {
4375 Self {
4376 symbol: symbol.to_string(),
4377 qty: qty.to_string(),
4378 }
4379 }
4380}
4381
4382#[derive(Debug, Serialize, Deserialize, Clone)]
4384pub struct LocateResponse {
4385 pub symbol: String,
4387 pub available_qty: String,
4389 pub rate: f64,
4391 #[serde(skip_serializing_if = "Option::is_none")]
4393 pub locate_id: Option<String>,
4394}
4395
4396#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
4398#[serde(rename_all = "lowercase")]
4399pub enum PdtStatus {
4400 No,
4402 Yes,
4404 Pending,
4406}
4407
4408#[derive(Debug, Clone)]
4410pub struct BuyingPowerCalculator {
4411 pub cash: f64,
4413 pub portfolio_value: f64,
4415 pub margin_multiplier: f64,
4417}
4418
4419impl BuyingPowerCalculator {
4420 #[must_use]
4422 pub fn new(cash: f64, portfolio_value: f64, margin_multiplier: f64) -> Self {
4423 Self {
4424 cash,
4425 portfolio_value,
4426 margin_multiplier,
4427 }
4428 }
4429
4430 #[must_use]
4432 pub fn buying_power(&self) -> f64 {
4433 self.cash * self.margin_multiplier
4434 }
4435
4436 #[must_use]
4438 pub fn max_shares(&self, price: f64) -> u64 {
4439 if price <= 0.0 {
4440 return 0;
4441 }
4442 (self.buying_power() / price).floor() as u64
4443 }
4444}
4445
4446#[derive(Debug, Clone, PartialEq)]
4452pub struct FractionalQty {
4453 value: f64,
4455}
4456
4457impl FractionalQty {
4458 pub const MIN: f64 = 0.000001;
4460
4461 #[must_use]
4469 pub fn new(value: f64) -> Option<Self> {
4470 if value >= Self::MIN {
4471 Some(Self { value })
4472 } else {
4473 None
4474 }
4475 }
4476
4477 #[must_use]
4479 pub fn value(&self) -> f64 {
4480 self.value
4481 }
4482
4483 #[must_use]
4485 pub fn is_whole(&self) -> bool {
4486 (self.value - self.value.round()).abs() < Self::MIN
4487 }
4488
4489 #[must_use]
4491 pub fn to_whole(&self) -> u64 {
4492 self.value.floor() as u64
4493 }
4494}
4495
4496impl std::fmt::Display for FractionalQty {
4497 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4498 write!(f, "{:.6}", self.value)
4499 }
4500}
4501
4502#[derive(Debug, Serialize, Deserialize, Clone)]
4504pub struct NotionalAmount {
4505 pub amount: String,
4507}
4508
4509impl NotionalAmount {
4510 pub const MIN: f64 = 1.0;
4512
4513 #[must_use]
4515 pub fn new(amount: &str) -> Self {
4516 Self {
4517 amount: amount.to_string(),
4518 }
4519 }
4520
4521 #[must_use]
4523 pub fn from_f64(amount: f64) -> Option<Self> {
4524 if amount >= Self::MIN {
4525 Some(Self {
4526 amount: format!("{:.2}", amount),
4527 })
4528 } else {
4529 None
4530 }
4531 }
4532
4533 #[must_use]
4535 pub fn to_f64(&self) -> Option<f64> {
4536 self.amount.parse().ok()
4537 }
4538
4539 #[must_use]
4541 pub fn is_valid(&self) -> bool {
4542 self.to_f64().is_some_and(|v| v >= Self::MIN)
4543 }
4544}
4545
4546#[derive(Debug, Clone)]
4548pub struct FractionalValidator {
4549 pub min_order_size: f64,
4551 pub min_trade_increment: f64,
4553}
4554
4555impl Default for FractionalValidator {
4556 fn default() -> Self {
4557 Self {
4558 min_order_size: 0.000001,
4559 min_trade_increment: 0.000001,
4560 }
4561 }
4562}
4563
4564impl FractionalValidator {
4565 #[must_use]
4567 pub fn new(min_order_size: f64, min_trade_increment: f64) -> Self {
4568 Self {
4569 min_order_size,
4570 min_trade_increment,
4571 }
4572 }
4573
4574 #[must_use]
4576 pub fn validate_qty(&self, qty: f64) -> bool {
4577 qty >= self.min_order_size
4578 }
4579
4580 #[must_use]
4582 pub fn validate_notional(&self, amount: f64) -> bool {
4583 amount >= NotionalAmount::MIN
4584 }
4585
4586 #[must_use]
4588 pub fn round_qty(&self, qty: f64) -> f64 {
4589 let increments = (qty / self.min_trade_increment).floor();
4590 increments * self.min_trade_increment
4591 }
4592}
4593
4594#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4596pub enum FractionalOrderRestriction {
4597 #[default]
4599 MarketOnly,
4600 MarketAndLimit,
4602 All,
4604}
4605
4606#[derive(Debug, Clone)]
4612pub struct PaperTradingConfig {
4613 pub initial_cash: f64,
4615 pub reset_on_create: bool,
4617}
4618
4619impl Default for PaperTradingConfig {
4620 fn default() -> Self {
4621 Self {
4622 initial_cash: 100_000.0,
4623 reset_on_create: false,
4624 }
4625 }
4626}
4627
4628impl PaperTradingConfig {
4629 #[must_use]
4631 pub fn new() -> Self {
4632 Self::default()
4633 }
4634
4635 #[must_use]
4637 pub fn initial_cash(mut self, cash: f64) -> Self {
4638 self.initial_cash = cash;
4639 self
4640 }
4641
4642 #[must_use]
4644 pub fn reset_on_create(mut self, reset: bool) -> Self {
4645 self.reset_on_create = reset;
4646 self
4647 }
4648}
4649
4650#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4652pub enum TradingEnvironment {
4653 Live,
4655 #[default]
4657 Paper,
4658}
4659
4660impl TradingEnvironment {
4661 #[must_use]
4663 pub fn is_paper(&self) -> bool {
4664 matches!(self, Self::Paper)
4665 }
4666
4667 #[must_use]
4669 pub fn is_live(&self) -> bool {
4670 matches!(self, Self::Live)
4671 }
4672
4673 #[must_use]
4675 pub fn base_url(&self) -> &'static str {
4676 match self {
4677 Self::Live => "https://api.alpaca.markets",
4678 Self::Paper => "https://paper-api.alpaca.markets",
4679 }
4680 }
4681
4682 #[must_use]
4684 pub fn data_url(&self) -> &'static str {
4685 "https://data.alpaca.markets"
4686 }
4687
4688 #[must_use]
4690 pub fn from_api_key(api_key: &str) -> Self {
4691 if api_key.starts_with("PK") {
4692 Self::Paper
4693 } else {
4694 Self::Live
4695 }
4696 }
4697}
4698
4699#[derive(Debug, Serialize, Deserialize, Clone, Default)]
4701pub struct ResetAccountRequest {
4702 #[serde(skip_serializing_if = "Option::is_none")]
4704 pub cash: Option<String>,
4705}
4706
4707impl ResetAccountRequest {
4708 #[must_use]
4710 pub fn new() -> Self {
4711 Self::default()
4712 }
4713
4714 #[must_use]
4716 pub fn cash(mut self, cash: &str) -> Self {
4717 self.cash = Some(cash.to_string());
4718 self
4719 }
4720}
4721
4722#[derive(Debug, Clone)]
4724pub struct EnvironmentGuard {
4725 environment: TradingEnvironment,
4727 allow_live: bool,
4729}
4730
4731impl EnvironmentGuard {
4732 #[must_use]
4734 pub fn paper_only() -> Self {
4735 Self {
4736 environment: TradingEnvironment::Paper,
4737 allow_live: false,
4738 }
4739 }
4740
4741 #[must_use]
4743 pub fn allow_live(environment: TradingEnvironment) -> Self {
4744 Self {
4745 environment,
4746 allow_live: true,
4747 }
4748 }
4749
4750 #[must_use]
4752 pub fn is_allowed(&self) -> bool {
4753 self.allow_live || self.environment.is_paper()
4754 }
4755
4756 #[must_use]
4758 pub fn environment(&self) -> TradingEnvironment {
4759 self.environment
4760 }
4761}
4762
4763#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4769#[serde(rename_all = "snake_case")]
4770pub enum MarketSession {
4771 PreMarket,
4773 Regular,
4775 AfterHours,
4777 Closed,
4779}
4780
4781impl MarketSession {
4782 #[must_use]
4784 pub fn is_trading_allowed(&self) -> bool {
4785 !matches!(self, Self::Closed)
4786 }
4787
4788 #[must_use]
4790 pub fn is_regular(&self) -> bool {
4791 matches!(self, Self::Regular)
4792 }
4793}
4794
4795#[derive(Debug, Serialize, Deserialize, Clone)]
4797pub struct CalendarDay {
4798 pub date: String,
4800 pub open: String,
4802 pub close: String,
4804 #[serde(skip_serializing_if = "Option::is_none")]
4806 pub settlement_date: Option<String>,
4807 #[serde(skip_serializing_if = "Option::is_none")]
4809 pub session_open: Option<String>,
4810 #[serde(skip_serializing_if = "Option::is_none")]
4812 pub session_close: Option<String>,
4813}
4814
4815#[derive(Debug, Serialize, Deserialize, Clone)]
4817pub struct MarketClock {
4818 pub timestamp: String,
4820 pub is_open: bool,
4822 pub next_open: String,
4824 pub next_close: String,
4826}
4827
4828impl MarketClock {
4829 #[must_use]
4831 pub fn current_session(&self) -> MarketSession {
4832 if self.is_open {
4833 MarketSession::Regular
4834 } else {
4835 MarketSession::Closed
4836 }
4837 }
4838}
4839
4840#[derive(Debug, Serialize, Clone, Default)]
4842pub struct CalendarParams {
4843 #[serde(skip_serializing_if = "Option::is_none")]
4845 pub start: Option<String>,
4846 #[serde(skip_serializing_if = "Option::is_none")]
4848 pub end: Option<String>,
4849}
4850
4851impl CalendarParams {
4852 #[must_use]
4854 pub fn new() -> Self {
4855 Self::default()
4856 }
4857
4858 #[must_use]
4860 pub fn start(mut self, date: &str) -> Self {
4861 self.start = Some(date.to_string());
4862 self
4863 }
4864
4865 #[must_use]
4867 pub fn end(mut self, date: &str) -> Self {
4868 self.end = Some(date.to_string());
4869 self
4870 }
4871}
4872
4873#[derive(Debug, Clone)]
4875pub struct TradingDay {
4876 pub date: String,
4878 pub is_trading_day: bool,
4880}
4881
4882impl TradingDay {
4883 #[must_use]
4885 pub fn new(date: &str, is_trading_day: bool) -> Self {
4886 Self {
4887 date: date.to_string(),
4888 is_trading_day,
4889 }
4890 }
4891
4892 #[must_use]
4894 pub fn trading(date: &str) -> Self {
4895 Self::new(date, true)
4896 }
4897
4898 #[must_use]
4900 pub fn non_trading(date: &str) -> Self {
4901 Self::new(date, false)
4902 }
4903}
4904
4905#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
4911pub enum FixVersion {
4912 #[serde(rename = "FIX.4.2")]
4914 #[default]
4915 Fix42,
4916 #[serde(rename = "FIX.4.4")]
4918 Fix44,
4919}
4920
4921impl std::fmt::Display for FixVersion {
4922 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4923 match self {
4924 Self::Fix42 => write!(f, "FIX.4.2"),
4925 Self::Fix44 => write!(f, "FIX.4.4"),
4926 }
4927 }
4928}
4929
4930#[derive(Debug, Clone)]
4932pub struct FixSessionConfig {
4933 pub version: FixVersion,
4935 pub sender_comp_id: String,
4937 pub target_comp_id: String,
4939 pub host: String,
4941 pub port: u16,
4943 pub heartbeat_interval: u32,
4945 pub enable_logging: bool,
4947}
4948
4949impl FixSessionConfig {
4950 #[must_use]
4952 pub fn new(sender_comp_id: &str, target_comp_id: &str, host: &str, port: u16) -> Self {
4953 Self {
4954 version: FixVersion::default(),
4955 sender_comp_id: sender_comp_id.to_string(),
4956 target_comp_id: target_comp_id.to_string(),
4957 host: host.to_string(),
4958 port,
4959 heartbeat_interval: 30,
4960 enable_logging: true,
4961 }
4962 }
4963
4964 #[must_use]
4966 pub fn version(mut self, version: FixVersion) -> Self {
4967 self.version = version;
4968 self
4969 }
4970
4971 #[must_use]
4973 pub fn heartbeat_interval(mut self, seconds: u32) -> Self {
4974 self.heartbeat_interval = seconds;
4975 self
4976 }
4977
4978 #[must_use]
4980 pub fn enable_logging(mut self, enable: bool) -> Self {
4981 self.enable_logging = enable;
4982 self
4983 }
4984}
4985
4986#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4988pub enum FixMsgType {
4989 Heartbeat,
4991 Logon,
4993 Logout,
4995 NewOrderSingle,
4997 OrderCancelRequest,
4999 OrderCancelReplaceRequest,
5001 ExecutionReport,
5003 OrderCancelReject,
5005 MarketDataRequest,
5007 MarketDataSnapshot,
5009}
5010
5011impl FixMsgType {
5012 #[must_use]
5014 pub fn tag_value(&self) -> &'static str {
5015 match self {
5016 Self::Heartbeat => "0",
5017 Self::Logon => "A",
5018 Self::Logout => "5",
5019 Self::NewOrderSingle => "D",
5020 Self::OrderCancelRequest => "F",
5021 Self::OrderCancelReplaceRequest => "G",
5022 Self::ExecutionReport => "8",
5023 Self::OrderCancelReject => "9",
5024 Self::MarketDataRequest => "V",
5025 Self::MarketDataSnapshot => "W",
5026 }
5027 }
5028}
5029
5030#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5032pub enum FixSessionState {
5033 #[default]
5035 Disconnected,
5036 Connecting,
5038 LoggedOn,
5040 LoggingOut,
5042}
5043
5044#[derive(Debug, Clone, Default)]
5046pub struct FixSequenceNumbers {
5047 pub outgoing: u64,
5049 pub incoming: u64,
5051}
5052
5053impl FixSequenceNumbers {
5054 #[must_use]
5056 pub fn new() -> Self {
5057 Self::default()
5058 }
5059
5060 pub fn next_outgoing(&mut self) -> u64 {
5062 self.outgoing += 1;
5063 self.outgoing
5064 }
5065
5066 pub fn next_incoming(&mut self) -> u64 {
5068 self.incoming += 1;
5069 self.incoming
5070 }
5071
5072 pub fn reset(&mut self) {
5074 self.outgoing = 0;
5075 self.incoming = 0;
5076 }
5077}
5078
5079#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5085#[serde(rename_all = "snake_case")]
5086pub enum StatementType {
5087 AccountStatement,
5089 TradeConfirmation,
5091 TaxDocument,
5093}
5094
5095#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5097pub enum TaxFormType {
5098 #[serde(rename = "1099-B")]
5100 Form1099B,
5101 #[serde(rename = "1099-DIV")]
5103 Form1099Div,
5104 #[serde(rename = "1099-INT")]
5106 Form1099Int,
5107}
5108
5109#[derive(Debug, Serialize, Deserialize, Clone)]
5111pub struct AccountDocument {
5112 pub id: String,
5114 #[serde(skip_serializing_if = "Option::is_none")]
5116 pub account_id: Option<String>,
5117 pub document_type: StatementType,
5119 pub date: String,
5121 #[serde(skip_serializing_if = "Option::is_none")]
5123 pub url: Option<String>,
5124}
5125
5126#[derive(Debug, Serialize, Deserialize, Clone)]
5128pub struct TradeConfirmation {
5129 pub id: String,
5131 pub trade_date: String,
5133 pub settlement_date: String,
5135 pub symbol: String,
5137 pub side: String,
5139 pub qty: String,
5141 pub price: String,
5143 pub amount: String,
5145}
5146
5147#[derive(Debug, Serialize, Deserialize, Clone)]
5149pub struct TaxDocument {
5150 pub id: String,
5152 pub tax_year: u16,
5154 pub form_type: TaxFormType,
5156 #[serde(skip_serializing_if = "Option::is_none")]
5158 pub url: Option<String>,
5159}
5160
5161#[derive(Debug, Serialize, Clone, Default)]
5163pub struct DocumentParams {
5164 #[serde(skip_serializing_if = "Option::is_none")]
5166 pub start: Option<String>,
5167 #[serde(skip_serializing_if = "Option::is_none")]
5169 pub end: Option<String>,
5170 #[serde(skip_serializing_if = "Option::is_none")]
5172 pub document_type: Option<StatementType>,
5173}
5174
5175impl DocumentParams {
5176 #[must_use]
5178 pub fn new() -> Self {
5179 Self::default()
5180 }
5181
5182 #[must_use]
5184 pub fn start(mut self, date: &str) -> Self {
5185 self.start = Some(date.to_string());
5186 self
5187 }
5188
5189 #[must_use]
5191 pub fn end(mut self, date: &str) -> Self {
5192 self.end = Some(date.to_string());
5193 self
5194 }
5195
5196 #[must_use]
5198 pub fn document_type(mut self, doc_type: StatementType) -> Self {
5199 self.document_type = Some(doc_type);
5200 self
5201 }
5202}
5203
5204#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5210#[serde(rename_all = "UPPERCASE")]
5211#[derive(Default)]
5212pub enum Currency {
5213 #[default]
5215 Usd,
5216 Eur,
5218 Gbp,
5220 Cad,
5222 Aud,
5224 Jpy,
5226 Chf,
5228}
5229
5230impl std::fmt::Display for Currency {
5231 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5232 match self {
5233 Self::Usd => write!(f, "USD"),
5234 Self::Eur => write!(f, "EUR"),
5235 Self::Gbp => write!(f, "GBP"),
5236 Self::Cad => write!(f, "CAD"),
5237 Self::Aud => write!(f, "AUD"),
5238 Self::Jpy => write!(f, "JPY"),
5239 Self::Chf => write!(f, "CHF"),
5240 }
5241 }
5242}
5243
5244#[derive(Debug, Serialize, Deserialize, Clone)]
5246pub struct ExchangeRate {
5247 pub base: Currency,
5249 pub quote: Currency,
5251 pub rate: f64,
5253 pub timestamp: String,
5255}
5256
5257impl ExchangeRate {
5258 #[must_use]
5260 pub fn new(base: Currency, quote: Currency, rate: f64) -> Self {
5261 Self {
5262 base,
5263 quote,
5264 rate,
5265 timestamp: String::new(),
5266 }
5267 }
5268
5269 #[must_use]
5271 pub fn convert(&self, amount: f64) -> f64 {
5272 amount * self.rate
5273 }
5274
5275 #[must_use]
5277 pub fn inverse(&self) -> f64 {
5278 if self.rate != 0.0 {
5279 1.0 / self.rate
5280 } else {
5281 0.0
5282 }
5283 }
5284}
5285
5286#[derive(Debug, Clone)]
5288pub struct CurrencyPair {
5289 pub base: Currency,
5291 pub quote: Currency,
5293}
5294
5295impl CurrencyPair {
5296 #[must_use]
5298 pub fn new(base: Currency, quote: Currency) -> Self {
5299 Self { base, quote }
5300 }
5301
5302 #[must_use]
5304 pub fn as_string(&self) -> String {
5305 format!("{}/{}", self.base, self.quote)
5306 }
5307}
5308
5309#[derive(Debug, Serialize, Deserialize, Clone)]
5311pub struct LctPosition {
5312 pub symbol: String,
5314 pub qty: String,
5316 pub market_value_local: String,
5318 pub cost_basis_local: String,
5320 pub unrealized_pl_local: String,
5322 pub currency: Currency,
5324}
5325
5326#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5332#[serde(rename_all = "snake_case")]
5333pub enum IraAccountType {
5334 Traditional,
5336 Roth,
5338 Sep,
5340 Simple,
5342}
5343
5344impl std::fmt::Display for IraAccountType {
5345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5346 match self {
5347 Self::Traditional => write!(f, "Traditional"),
5348 Self::Roth => write!(f, "Roth"),
5349 Self::Sep => write!(f, "SEP"),
5350 Self::Simple => write!(f, "SIMPLE"),
5351 }
5352 }
5353}
5354
5355#[derive(Debug, Serialize, Deserialize, Clone)]
5357pub struct IraContribution {
5358 pub id: String,
5360 pub account_id: String,
5362 pub amount: String,
5364 pub tax_year: u16,
5366 pub date: String,
5368 #[serde(skip_serializing_if = "Option::is_none")]
5370 pub contribution_type: Option<String>,
5371}
5372
5373#[derive(Debug, Serialize, Deserialize, Clone)]
5375pub struct IraDistribution {
5376 pub id: String,
5378 pub account_id: String,
5380 pub amount: String,
5382 pub date: String,
5384 #[serde(skip_serializing_if = "Option::is_none")]
5386 pub reason: Option<String>,
5387 #[serde(skip_serializing_if = "Option::is_none")]
5389 pub federal_withholding: Option<String>,
5390 #[serde(skip_serializing_if = "Option::is_none")]
5392 pub state_withholding: Option<String>,
5393}
5394
5395#[derive(Debug, Serialize, Deserialize, Clone)]
5397pub struct IraBeneficiary {
5398 pub id: String,
5400 pub account_id: String,
5402 pub name: String,
5404 pub beneficiary_type: String,
5406 pub percentage: f64,
5408 #[serde(skip_serializing_if = "Option::is_none")]
5410 pub relationship: Option<String>,
5411}
5412
5413#[derive(Debug, Serialize, Clone)]
5415pub struct CreateIraContributionRequest {
5416 pub amount: String,
5418 pub tax_year: u16,
5420}
5421
5422impl CreateIraContributionRequest {
5423 #[must_use]
5425 pub fn new(amount: &str, tax_year: u16) -> Self {
5426 Self {
5427 amount: amount.to_string(),
5428 tax_year,
5429 }
5430 }
5431}
5432
5433#[derive(Debug, Serialize, Clone)]
5435pub struct CreateIraDistributionRequest {
5436 pub amount: String,
5438 #[serde(skip_serializing_if = "Option::is_none")]
5440 pub reason: Option<String>,
5441}
5442
5443impl CreateIraDistributionRequest {
5444 #[must_use]
5446 pub fn new(amount: &str) -> Self {
5447 Self {
5448 amount: amount.to_string(),
5449 reason: None,
5450 }
5451 }
5452
5453 #[must_use]
5455 pub fn reason(mut self, reason: &str) -> Self {
5456 self.reason = Some(reason.to_string());
5457 self
5458 }
5459}
5460
5461#[cfg(test)]
5462mod tests {
5463 use super::*;
5464
5465 #[test]
5466 fn test_take_profit_new() {
5467 let tp = TakeProfit::new("150.00");
5468 assert_eq!(tp.limit_price, "150.00");
5469 }
5470
5471 #[test]
5472 fn test_stop_loss_new() {
5473 let sl = StopLoss::new("95.00");
5474 assert_eq!(sl.stop_price, "95.00");
5475 assert!(sl.limit_price.is_none());
5476 }
5477
5478 #[test]
5479 fn test_stop_loss_with_limit() {
5480 let sl = StopLoss::with_limit("95.00", "94.50");
5481 assert_eq!(sl.stop_price, "95.00");
5482 assert_eq!(sl.limit_price, Some("94.50".to_string()));
5483 }
5484
5485 #[test]
5486 fn test_time_in_force_serialization() {
5487 let tif = TimeInForce::Gtc;
5488 let json = serde_json::to_string(&tif).unwrap();
5489 assert_eq!(json, "\"gtc\"");
5490
5491 let tif = TimeInForce::Gtd;
5492 let json = serde_json::to_string(&tif).unwrap();
5493 assert_eq!(json, "\"gtd\"");
5494 }
5495
5496 #[test]
5497 fn test_time_in_force_deserialization() {
5498 let tif: TimeInForce = serde_json::from_str("\"day\"").unwrap();
5499 assert_eq!(tif, TimeInForce::Day);
5500
5501 let tif: TimeInForce = serde_json::from_str("\"gtc\"").unwrap();
5502 assert_eq!(tif, TimeInForce::Gtc);
5503
5504 let tif: TimeInForce = serde_json::from_str("\"ioc\"").unwrap();
5505 assert_eq!(tif, TimeInForce::Ioc);
5506 }
5507
5508 #[test]
5509 fn test_order_class_serialization() {
5510 let oc = OrderClass::Bracket;
5511 let json = serde_json::to_string(&oc).unwrap();
5512 assert_eq!(json, "\"bracket\"");
5513
5514 let oc = OrderClass::Oco;
5515 let json = serde_json::to_string(&oc).unwrap();
5516 assert_eq!(json, "\"oco\"");
5517
5518 let oc = OrderClass::Oto;
5519 let json = serde_json::to_string(&oc).unwrap();
5520 assert_eq!(json, "\"oto\"");
5521 }
5522
5523 #[test]
5524 fn test_position_intent_serialization() {
5525 let pi = PositionIntent::BuyToOpen;
5526 let json = serde_json::to_string(&pi).unwrap();
5527 assert_eq!(json, "\"buy_to_open\"");
5528
5529 let pi = PositionIntent::SellToClose;
5530 let json = serde_json::to_string(&pi).unwrap();
5531 assert_eq!(json, "\"sell_to_close\"");
5532 }
5533
5534 #[test]
5535 fn test_order_query_status_serialization() {
5536 let status = OrderQueryStatus::Open;
5537 let json = serde_json::to_string(&status).unwrap();
5538 assert_eq!(json, "\"open\"");
5539
5540 let status = OrderQueryStatus::All;
5541 let json = serde_json::to_string(&status).unwrap();
5542 assert_eq!(json, "\"all\"");
5543 }
5544
5545 #[test]
5546 fn test_sort_direction_serialization() {
5547 let dir = SortDirection::Asc;
5548 let json = serde_json::to_string(&dir).unwrap();
5549 assert_eq!(json, "\"asc\"");
5550
5551 let dir = SortDirection::Desc;
5552 let json = serde_json::to_string(&dir).unwrap();
5553 assert_eq!(json, "\"desc\"");
5554 }
5555
5556 #[test]
5557 fn test_order_side_serialization() {
5558 let side = OrderSide::Buy;
5559 let json = serde_json::to_string(&side).unwrap();
5560 assert_eq!(json, "\"buy\"");
5561
5562 let side = OrderSide::Sell;
5563 let json = serde_json::to_string(&side).unwrap();
5564 assert_eq!(json, "\"sell\"");
5565 }
5566
5567 #[test]
5568 fn test_order_type_serialization() {
5569 let ot = OrderType::Market;
5570 let json = serde_json::to_string(&ot).unwrap();
5571 assert_eq!(json, "\"market\"");
5572
5573 let ot = OrderType::StopLimit;
5574 let json = serde_json::to_string(&ot).unwrap();
5575 assert_eq!(json, "\"stop_limit\"");
5576
5577 let ot = OrderType::TrailingStop;
5578 let json = serde_json::to_string(&ot).unwrap();
5579 assert_eq!(json, "\"trailing_stop\"");
5580 }
5581
5582 #[test]
5583 fn test_option_type_serialization() {
5584 let ot = OptionType::Call;
5585 let json = serde_json::to_string(&ot).unwrap();
5586 assert_eq!(json, "\"call\"");
5587
5588 let ot = OptionType::Put;
5589 let json = serde_json::to_string(&ot).unwrap();
5590 assert_eq!(json, "\"put\"");
5591 }
5592
5593 #[test]
5594 fn test_option_style_serialization() {
5595 let style = OptionStyle::American;
5596 let json = serde_json::to_string(&style).unwrap();
5597 assert_eq!(json, "\"american\"");
5598
5599 let style = OptionStyle::European;
5600 let json = serde_json::to_string(&style).unwrap();
5601 assert_eq!(json, "\"european\"");
5602 }
5603
5604 #[test]
5605 fn test_options_approval_level_serialization() {
5606 let level = OptionsApprovalLevel::Level1;
5607 let json = serde_json::to_string(&level).unwrap();
5608 assert_eq!(json, "\"1\"");
5609
5610 let level = OptionsApprovalLevel::Level3;
5611 let json = serde_json::to_string(&level).unwrap();
5612 assert_eq!(json, "\"3\"");
5613 }
5614
5615 #[test]
5616 fn test_option_contract_params_builder() {
5617 let params = OptionContractParams::new()
5618 .underlying_symbol("AAPL")
5619 .expiration_date("2024-03-15")
5620 .option_type(OptionType::Call)
5621 .limit(10);
5622
5623 assert_eq!(params.underlying_symbol, Some("AAPL".to_string()));
5624 assert_eq!(params.expiration_date, Some("2024-03-15".to_string()));
5625 assert_eq!(params.option_type, Some(OptionType::Call));
5626 assert_eq!(params.limit, Some(10));
5627 }
5628
5629 #[test]
5630 fn test_option_bars_params_builder() {
5631 let params = OptionBarsParams::new("AAPL240315C00150000")
5632 .timeframe("1Day")
5633 .time_range("2024-01-01", "2024-03-01")
5634 .limit(100);
5635
5636 assert_eq!(params.symbols, Some("AAPL240315C00150000".to_string()));
5637 assert_eq!(params.timeframe, Some("1Day".to_string()));
5638 assert_eq!(params.start, Some("2024-01-01".to_string()));
5639 assert_eq!(params.end, Some("2024-03-01".to_string()));
5640 assert_eq!(params.limit, Some(100));
5641 }
5642
5643 #[test]
5644 fn test_data_feed_serialization() {
5645 let feed = DataFeed::Sip;
5646 let json = serde_json::to_string(&feed).unwrap();
5647 assert_eq!(json, "\"sip\"");
5648
5649 let feed = DataFeed::Iex;
5650 let json = serde_json::to_string(&feed).unwrap();
5651 assert_eq!(json, "\"iex\"");
5652 }
5653
5654 #[test]
5655 fn test_corporate_action_type_serialization() {
5656 let action = CorporateActionType::Dividend;
5657 let json = serde_json::to_string(&action).unwrap();
5658 assert_eq!(json, "\"dividend\"");
5659
5660 let action = CorporateActionType::Split;
5661 let json = serde_json::to_string(&action).unwrap();
5662 assert_eq!(json, "\"split\"");
5663 }
5664
5665 #[test]
5666 fn test_multi_bars_params_builder() {
5667 let params = MultiBarsParams::new("AAPL,MSFT,GOOGL")
5668 .timeframe("1Day")
5669 .time_range("2024-01-01", "2024-03-01")
5670 .feed(DataFeed::Sip)
5671 .limit(100);
5672
5673 assert_eq!(params.symbols, Some("AAPL,MSFT,GOOGL".to_string()));
5674 assert_eq!(params.timeframe, Some("1Day".to_string()));
5675 assert_eq!(params.feed, Some(DataFeed::Sip));
5676 assert_eq!(params.limit, Some(100));
5677 }
5678
5679 #[test]
5680 fn test_corporate_actions_params_builder() {
5681 let params = CorporateActionsParams::new()
5682 .symbols("AAPL,MSFT")
5683 .types("dividend,split")
5684 .date_range("2024-01-01", "2024-12-31")
5685 .limit(50);
5686
5687 assert_eq!(params.symbols, Some("AAPL,MSFT".to_string()));
5688 assert_eq!(params.types, Some("dividend,split".to_string()));
5689 assert_eq!(params.start, Some("2024-01-01".to_string()));
5690 assert_eq!(params.limit, Some(50));
5691 }
5692
5693 #[test]
5694 fn test_broker_account_status_serialization() {
5695 let status = BrokerAccountStatus::Active;
5696 let json = serde_json::to_string(&status).unwrap();
5697 assert_eq!(json, "\"ACTIVE\"");
5698
5699 let status = BrokerAccountStatus::Onboarding;
5700 let json = serde_json::to_string(&status).unwrap();
5701 assert_eq!(json, "\"ONBOARDING\"");
5702 }
5703
5704 #[test]
5705 fn test_agreement_type_serialization() {
5706 let agreement = AgreementType::CustomerAgreement;
5707 let json = serde_json::to_string(&agreement).unwrap();
5708 assert_eq!(json, "\"customer_agreement\"");
5709 }
5710
5711 #[test]
5712 fn test_contact_builder() {
5713 let contact = Contact::new("test@example.com", "New York", "10001", "USA")
5714 .phone("+1234567890")
5715 .street("123 Main St")
5716 .state("NY");
5717
5718 assert_eq!(contact.email_address, "test@example.com");
5719 assert_eq!(contact.city, "New York");
5720 assert_eq!(contact.phone_number, Some("+1234567890".to_string()));
5721 assert_eq!(contact.state, Some("NY".to_string()));
5722 }
5723
5724 #[test]
5725 fn test_identity_builder() {
5726 let identity = Identity::new("John", "Doe", "1990-01-15")
5727 .tax_id("123-45-6789", TaxIdType::UsaSsn)
5728 .citizenship("USA");
5729
5730 assert_eq!(identity.given_name, "John");
5731 assert_eq!(identity.family_name, "Doe");
5732 assert_eq!(identity.tax_id, Some("123-45-6789".to_string()));
5733 assert_eq!(identity.tax_id_type, Some(TaxIdType::UsaSsn));
5734 }
5735
5736 #[test]
5737 fn test_disclosures_builder() {
5738 let disclosures = Disclosures::new().control_person(false).employment(
5739 "employed",
5740 "Acme Corp",
5741 "Engineer",
5742 );
5743
5744 assert!(!disclosures.is_control_person);
5745 assert_eq!(disclosures.employer_name, Some("Acme Corp".to_string()));
5746 }
5747
5748 #[test]
5749 fn test_transfer_status_serialization() {
5750 let status = TransferStatus::Complete;
5751 let json = serde_json::to_string(&status).unwrap();
5752 assert_eq!(json, "\"COMPLETE\"");
5753 }
5754
5755 #[test]
5756 fn test_create_ach_relationship_request() {
5757 let request = CreateAchRelationshipRequest::new(
5758 "John Doe",
5759 BankAccountType::Checking,
5760 "123456789",
5761 "021000021",
5762 )
5763 .nickname("Primary Account");
5764
5765 assert_eq!(request.account_owner_name, "John Doe");
5766 assert_eq!(request.bank_account_type, BankAccountType::Checking);
5767 assert_eq!(request.nickname, Some("Primary Account".to_string()));
5768 }
5769
5770 #[test]
5771 fn test_create_transfer_request_ach() {
5772 let request = CreateTransferRequest::ach("rel-123", "1000.00", TransferDirection::Incoming);
5773
5774 assert_eq!(request.transfer_type, TransferType::Ach);
5775 assert_eq!(request.relationship_id, Some("rel-123".to_string()));
5776 assert_eq!(request.amount, "1000.00");
5777 assert_eq!(request.direction, TransferDirection::Incoming);
5778 }
5779
5780 #[test]
5781 fn test_create_journal_request_cash() {
5782 let request =
5783 CreateJournalRequest::cash("acc-from", "acc-to", "500.00").description("Test transfer");
5784
5785 assert_eq!(request.entry_type, JournalEntryType::Jnlc);
5786 assert_eq!(request.amount, Some("500.00".to_string()));
5787 assert_eq!(request.description, Some("Test transfer".to_string()));
5788 }
5789
5790 #[test]
5791 fn test_crypto_chain_serialization() {
5792 let chain = CryptoChain::Eth;
5793 let json = serde_json::to_string(&chain).unwrap();
5794 assert_eq!(json, "\"ETH\"");
5795 }
5796
5797 #[test]
5798 fn test_crypto_transfer_status_serialization() {
5799 let status = CryptoTransferStatus::Complete;
5800 let json = serde_json::to_string(&status).unwrap();
5801 assert_eq!(json, "\"COMPLETE\"");
5802 }
5803
5804 #[test]
5805 fn test_create_crypto_whitelist_request() {
5806 let request =
5807 CreateCryptoWhitelistRequest::new("BTC", "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh")
5808 .label("My Hardware Wallet");
5809
5810 assert_eq!(request.asset, "BTC");
5811 assert_eq!(request.label, Some("My Hardware Wallet".to_string()));
5812 }
5813
5814 #[test]
5815 fn test_crypto_bars_params_builder() {
5816 let params = CryptoBarsParams::new("BTC/USD,ETH/USD")
5817 .timeframe("1Hour")
5818 .limit(100);
5819
5820 assert_eq!(params.symbols, Some("BTC/USD,ETH/USD".to_string()));
5821 assert_eq!(params.timeframe, Some("1Hour".to_string()));
5822 assert_eq!(params.limit, Some(100));
5823 }
5824
5825 #[test]
5826 fn test_news_content_type_serialization() {
5827 let content_type = NewsContentType::Article;
5828 let json = serde_json::to_string(&content_type).unwrap();
5829 assert_eq!(json, "\"article\"");
5830 }
5831
5832 #[test]
5833 fn test_news_params_builder() {
5834 let params = NewsParams::new()
5835 .symbols("AAPL,MSFT")
5836 .sort_desc()
5837 .with_content()
5838 .limit(50);
5839
5840 assert_eq!(params.symbols, Some("AAPL,MSFT".to_string()));
5841 assert_eq!(params.sort, Some("desc".to_string()));
5842 assert_eq!(params.include_content, Some(true));
5843 assert_eq!(params.limit, Some(50));
5844 }
5845
5846 #[test]
5847 fn test_oauth_scope_display() {
5848 assert_eq!(OAuthScope::AccountWrite.to_string(), "account:write");
5849 assert_eq!(OAuthScope::Trading.to_string(), "trading");
5850 assert_eq!(OAuthScope::Data.to_string(), "data");
5851 }
5852
5853 #[test]
5854 fn test_oauth_config_builder() {
5855 let config = OAuthConfig::new("client123", "secret456", "https://example.com/callback")
5856 .scope(OAuthScope::Trading)
5857 .scope(OAuthScope::Data);
5858
5859 assert_eq!(config.client_id, "client123");
5860 assert_eq!(config.scopes.len(), 2);
5861 }
5862
5863 #[test]
5864 fn test_oauth_token_authorization_header() {
5865 let token = crate::OAuthToken {
5866 access_token: "abc123".to_string(),
5867 refresh_token: Some("refresh456".to_string()),
5868 token_type: "Bearer".to_string(),
5869 expires_in: Some(3600),
5870 scope: Some("trading data".to_string()),
5871 };
5872
5873 assert_eq!(token.auth_header(), "Bearer abc123");
5874 assert!(token.has_refresh_token());
5875 }
5876
5877 #[test]
5878 fn test_account_status_event_type_serialization() {
5879 let event_type = AccountStatusEventType::AccountApproved;
5880 let json = serde_json::to_string(&event_type).unwrap();
5881 assert_eq!(json, "\"ACCOUNT_APPROVED\"");
5882 }
5883
5884 #[test]
5885 fn test_sse_event_params_builder() {
5886 let params = SseEventParams::new()
5887 .account_id("acc-123")
5888 .since("2024-01-01T00:00:00Z");
5889
5890 assert_eq!(params.account_id, Some("acc-123".to_string()));
5891 assert_eq!(params.since, Some("2024-01-01T00:00:00Z".to_string()));
5892 }
5893
5894 #[test]
5895 fn test_asset_status_serialization() {
5896 let status = AssetStatus::Active;
5897 let json = serde_json::to_string(&status).unwrap();
5898 assert_eq!(json, "\"active\"");
5899 }
5900
5901 #[test]
5902 fn test_list_assets_params_builder() {
5903 let params = ListAssetsParams::new()
5904 .status(AssetStatus::Active)
5905 .asset_class("us_equity")
5906 .exchange("NYSE");
5907
5908 assert_eq!(params.status, Some(AssetStatus::Active));
5909 assert_eq!(params.asset_class, Some("us_equity".to_string()));
5910 assert_eq!(params.exchange, Some("NYSE".to_string()));
5911 }
5912
5913 #[test]
5914 fn test_activity_type_serialization() {
5915 let activity = ActivityType::Fill;
5916 let json = serde_json::to_string(&activity).unwrap();
5917 assert_eq!(json, "\"FILL\"");
5918
5919 let div = ActivityType::Div;
5920 let json = serde_json::to_string(&div).unwrap();
5921 assert_eq!(json, "\"DIV\"");
5922 }
5923
5924 #[test]
5925 fn test_list_activities_params_builder() {
5926 let params = ListActivitiesParams::new()
5927 .activity_types("FILL,DIV")
5928 .direction(SortDirection::Desc)
5929 .page_size(100);
5930
5931 assert_eq!(params.activity_types, Some("FILL,DIV".to_string()));
5932 assert_eq!(params.direction, Some(SortDirection::Desc));
5933 assert_eq!(params.page_size, Some(100));
5934 }
5935
5936 #[test]
5937 fn test_portfolio_history_params_builder() {
5938 let params = PortfolioHistoryParams::new()
5939 .period(PortfolioPeriod::OneMonth)
5940 .timeframe(PortfolioTimeframe::OneDay)
5941 .extended_hours(true);
5942
5943 assert_eq!(params.period, Some("1M".to_string()));
5944 assert_eq!(params.timeframe, Some("1D".to_string()));
5945 assert_eq!(params.extended_hours, Some(true));
5946 }
5947
5948 #[test]
5949 fn test_target_allocation_percent() {
5950 let alloc = TargetAllocation::percent("AAPL", 25.0);
5951 assert_eq!(alloc.symbol, "AAPL");
5952 assert_eq!(alloc.percent, Some(25.0));
5953 assert!(alloc.notional.is_none());
5954 }
5955
5956 #[test]
5957 fn test_rate_limit_config_builder() {
5958 let config = RateLimitConfig::new()
5959 .requests_per_minute(100)
5960 .burst_limit(25)
5961 .max_retries(5);
5962
5963 assert_eq!(config.requests_per_minute, 100);
5964 assert_eq!(config.burst_limit, 25);
5965 assert_eq!(config.max_retries, 5);
5966 }
5967
5968 #[test]
5969 fn test_rate_limit_status() {
5970 let status = RateLimitStatus::new(50, 200, 1704067200);
5971 assert!(!status.is_rate_limited());
5972
5973 let limited = RateLimitStatus::new(0, 200, 1704067200);
5974 assert!(limited.is_rate_limited());
5975 }
5976
5977 #[test]
5978 fn test_margin_requirement_calculations() {
5979 let req = MarginRequirement::standard();
5980 assert!((req.initial - 0.50).abs() < f64::EPSILON);
5981 assert!((req.maintenance - 0.25).abs() < f64::EPSILON);
5982
5983 let initial = req.calculate_initial_margin(10000.0);
5984 assert!((initial - 5000.0).abs() < f64::EPSILON);
5985
5986 let maintenance = req.calculate_maintenance_margin(10000.0);
5987 assert!((maintenance - 2500.0).abs() < f64::EPSILON);
5988 }
5989
5990 #[test]
5991 fn test_buying_power_calculator() {
5992 let calc = BuyingPowerCalculator::new(10000.0, 50000.0, 2.0);
5993 assert!((calc.buying_power() - 20000.0).abs() < f64::EPSILON);
5994 assert_eq!(calc.max_shares(100.0), 200);
5995 assert_eq!(calc.max_shares(0.0), 0);
5996 }
5997
5998 #[test]
5999 fn test_fractional_qty() {
6000 let qty = FractionalQty::new(1.5).unwrap();
6001 assert!((qty.value() - 1.5).abs() < f64::EPSILON);
6002 assert!(!qty.is_whole());
6003 assert_eq!(qty.to_whole(), 1);
6004
6005 let whole = FractionalQty::new(5.0).unwrap();
6006 assert!(whole.is_whole());
6007
6008 assert!(FractionalQty::new(0.0).is_none());
6009 }
6010
6011 #[test]
6012 fn test_notional_amount() {
6013 let amount = NotionalAmount::from_f64(100.50).unwrap();
6014 assert_eq!(amount.amount, "100.50");
6015 assert!(amount.is_valid());
6016
6017 assert!(NotionalAmount::from_f64(0.5).is_none());
6018 }
6019
6020 #[test]
6021 fn test_trading_environment() {
6022 let paper = TradingEnvironment::Paper;
6023 assert!(paper.is_paper());
6024 assert!(!paper.is_live());
6025 assert_eq!(paper.base_url(), "https://paper-api.alpaca.markets");
6026
6027 let live = TradingEnvironment::Live;
6028 assert!(live.is_live());
6029 assert!(!live.is_paper());
6030
6031 assert_eq!(
6032 TradingEnvironment::from_api_key("PKABC123"),
6033 TradingEnvironment::Paper
6034 );
6035 assert_eq!(
6036 TradingEnvironment::from_api_key("AKABC123"),
6037 TradingEnvironment::Live
6038 );
6039 }
6040
6041 #[test]
6042 fn test_environment_guard() {
6043 let guard = EnvironmentGuard::paper_only();
6044 assert!(guard.is_allowed());
6045 assert!(guard.environment().is_paper());
6046
6047 let live_guard = EnvironmentGuard::allow_live(TradingEnvironment::Live);
6048 assert!(live_guard.is_allowed());
6049 }
6050
6051 #[test]
6052 fn test_market_session() {
6053 let regular = MarketSession::Regular;
6054 assert!(regular.is_trading_allowed());
6055 assert!(regular.is_regular());
6056
6057 let closed = MarketSession::Closed;
6058 assert!(!closed.is_trading_allowed());
6059 assert!(!closed.is_regular());
6060 }
6061
6062 #[test]
6063 fn test_calendar_params_builder() {
6064 let params = CalendarParams::new().start("2024-01-01").end("2024-12-31");
6065 assert_eq!(params.start, Some("2024-01-01".to_string()));
6066 assert_eq!(params.end, Some("2024-12-31".to_string()));
6067 }
6068
6069 #[test]
6070 fn test_fix_session_config() {
6071 let config = FixSessionConfig::new("SENDER", "TARGET", "localhost", 9876)
6072 .version(FixVersion::Fix44)
6073 .heartbeat_interval(60);
6074 assert_eq!(config.sender_comp_id, "SENDER");
6075 assert_eq!(config.target_comp_id, "TARGET");
6076 assert_eq!(config.version, FixVersion::Fix44);
6077 assert_eq!(config.heartbeat_interval, 60);
6078 }
6079
6080 #[test]
6081 fn test_fix_sequence_numbers() {
6082 let mut seq = FixSequenceNumbers::new();
6083 assert_eq!(seq.next_outgoing(), 1);
6084 assert_eq!(seq.next_outgoing(), 2);
6085 assert_eq!(seq.next_incoming(), 1);
6086 seq.reset();
6087 assert_eq!(seq.outgoing, 0);
6088 assert_eq!(seq.incoming, 0);
6089 }
6090
6091 #[test]
6092 fn test_document_params_builder() {
6093 let params = DocumentParams::new()
6094 .start("2024-01-01")
6095 .end("2024-12-31")
6096 .document_type(StatementType::AccountStatement);
6097 assert_eq!(params.start, Some("2024-01-01".to_string()));
6098 assert_eq!(params.end, Some("2024-12-31".to_string()));
6099 assert_eq!(params.document_type, Some(StatementType::AccountStatement));
6100 }
6101
6102 #[test]
6103 fn test_exchange_rate_conversion() {
6104 let rate = ExchangeRate::new(Currency::Eur, Currency::Usd, 1.10);
6105 assert!((rate.convert(100.0) - 110.0).abs() < 0.0001);
6106 assert!((rate.inverse() - 0.909_090_909_090_909_1).abs() < 0.0001);
6107 }
6108
6109 #[test]
6110 fn test_currency_pair() {
6111 let pair = CurrencyPair::new(Currency::Eur, Currency::Usd);
6112 assert_eq!(pair.as_string(), "EUR/USD");
6113 }
6114
6115 #[test]
6116 fn test_ira_account_type_display() {
6117 assert_eq!(IraAccountType::Traditional.to_string(), "Traditional");
6118 assert_eq!(IraAccountType::Roth.to_string(), "Roth");
6119 assert_eq!(IraAccountType::Sep.to_string(), "SEP");
6120 assert_eq!(IraAccountType::Simple.to_string(), "SIMPLE");
6121 }
6122
6123 #[test]
6124 fn test_create_ira_contribution_request() {
6125 let req = CreateIraContributionRequest::new("5000.00", 2024);
6126 assert_eq!(req.amount, "5000.00");
6127 assert_eq!(req.tax_year, 2024);
6128 }
6129}