tradestation_rs/
account.rs

1use crate::responses::account::{StreamOrdersResp, StreamPositionsResp};
2use crate::{responses::account as responses, Client, Error};
3use serde::{Deserialize, Serialize};
4use std::{error::Error as StdErrorTrait, future::Future, pin::Pin};
5
6#[derive(Clone, Debug, Deserialize, Serialize)]
7#[serde(rename_all = "PascalCase")]
8/// TradeStation Account
9pub struct Account {
10    #[serde(rename = "AccountID")]
11    /// The main identifier for a TradeStation account.
12    account_id: String,
13    /// The currency the account is based on.
14    currency: String,
15    /// The type of account, examples: "Cash" or "Margin"
16    // TODO: Make enum for this
17    account_type: String,
18    /// The account details, stuff like options level and day trading approval
19    ///
20    /// NOTE: This will always be `None` if it's a Futures `Account`
21    account_detail: Option<AccountDetail>,
22}
23impl Account {
24    /// Get a specific TradeStation `Account` by it's account id.
25    pub async fn get(client: &mut Client, account_id: &str) -> Result<Account, Error> {
26        if let Some(account) = Account::get_all(client)
27            .await?
28            .iter()
29            .find(|account| account.account_id == account_id)
30        {
31            Ok(account.clone())
32        } else {
33            Err(Error::AccountNotFound)
34        }
35    }
36
37    /// Get all of your registered TradeStation `Account`(s).
38    pub async fn get_all(client: &mut Client) -> Result<Vec<Account>, Error> {
39        let endpoint = "brokerage/accounts";
40
41        let resp = client
42            .get(endpoint)
43            .await?
44            .json::<responses::GetAccountsResp>()
45            .await?;
46
47        Ok(resp.accounts)
48    }
49
50    /// Get the current balance of an `Account`.
51    pub async fn get_balance(&self, client: &mut Client) -> Result<Balance, Error> {
52        let endpoint = format!("brokerage/accounts/{}/balances", self.account_id);
53
54        if let Some(balance) = client
55            .get(&endpoint)
56            .await?
57            .json::<responses::GetBalanceResp>()
58            .await?
59            .balances
60            .pop()
61        {
62            Ok(balance)
63        } else {
64            Err(Error::AccountNotFound)
65        }
66    }
67
68    /// Get the current balance of all `Account`(s) by account ids.
69    ///
70    /// NOTE: If you have `Vec<Account>` you should instead use `Vec<Account>::get_balances()`
71    /// this method should only be used in cases where you ONLY have account id's.
72    pub async fn get_balances_by_ids(
73        client: &mut Client,
74        account_ids: Vec<&str>,
75    ) -> Result<Vec<Balance>, Error> {
76        let endpoint = format!("brokerage/accounts/{}/balances", account_ids.join(","));
77
78        let resp = client
79            .get(&endpoint)
80            .await?
81            .json::<responses::GetBalanceResp>()
82            .await?;
83
84        Ok(resp.balances)
85    }
86
87    /// Get the beginning of day balance of an `Account`.
88    pub async fn get_bod_balance(&self, client: &mut Client) -> Result<BODBalance, Error> {
89        let endpoint = format!("brokerage/accounts/{}/bodbalances", self.account_id);
90
91        if let Some(balance) = client
92            .get(&endpoint)
93            .await?
94            .json::<responses::GetBODBalanceResp>()
95            .await?
96            .bod_balances
97            .pop()
98        {
99            Ok(balance)
100        } else {
101            Err(Error::AccountNotFound)
102        }
103    }
104
105    /// Get the beginning of day balances for multiple `Account`(s) by account id.
106    ///
107    /// NOTE: If you have `Vec<Account>` you should instead use `Vec<Account>::get_bod_balances()`
108    /// this method should only be used if you ONLY have account id's.
109    pub async fn get_bod_balances_by_ids(
110        client: &mut Client,
111        account_ids: Vec<&str>,
112    ) -> Result<Vec<BODBalance>, Error> {
113        let endpoint = format!("brokerage/accounts/{}/bodbalances", account_ids.join(","));
114
115        let resp = client
116            .get(&endpoint)
117            .await?
118            .json::<responses::GetBODBalanceResp>()
119            .await?;
120
121        Ok(resp.bod_balances)
122    }
123
124    /// Fetches Historical `Order`(s) since a specific date for the given `Account`.
125    ///
126    /// NOTE: Date format is {YEAR-MONTH-DAY} ex: `"2024-07-09"`, and is limited to 90
127    /// days prior to the current date.
128    ///
129    /// NOTE: Excludes open `Order`(s) and is sorted in descending order of time closed.
130    pub async fn get_historic_orders(
131        &self,
132        client: &mut Client,
133        since_date: &str,
134    ) -> Result<Vec<Order>, Error> {
135        let endpoint = format!(
136            "brokerage/accounts/{}/historicalorders?since={}",
137            self.account_id, since_date
138        );
139
140        let resp = client
141            .get(&endpoint)
142            .await?
143            .json::<responses::GetOrdersResp>()
144            .await?;
145
146        Ok(resp.orders)
147    }
148
149    /// Fetches Historical `Order`(s) for the given `Account`(s) by id.
150    ///
151    /// NOTE: Date format is {YEAR-MONTH-DAY} ex: `"2024-07-09"`, and is limited to 90
152    /// days prior to the current date.
153    ///
154    /// NOTE: Excludes open `Order`(s) and is sorted in descending order of time closed.
155    pub async fn get_historic_orders_by_ids(
156        client: &mut Client,
157        account_ids: Vec<&str>,
158        since_date: &str,
159    ) -> Result<Vec<Order>, Error> {
160        let endpoint = format!(
161            "brokerage/accounts/{}/historicalorders?since={}",
162            account_ids.join(","),
163            since_date,
164        );
165
166        let resp = client
167            .get(&endpoint)
168            .await?
169            .json::<responses::GetOrdersResp>()
170            .await?;
171
172        Ok(resp.orders)
173    }
174
175    /// Fetches orders for the given `Account`.
176    ///
177    /// # Example
178    /// ---
179    ///
180    /// Grab all the orders for a specific account. Say you need to go
181    /// through all the orders your algorithm placed today and filter out
182    /// only the orders that were filled for data storage purposes.
183    ///
184    /// ```ignore
185    /// // Initialize the client
186    /// let mut client = ClientBuilder::new()?
187    ///     .credentials("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")?
188    ///     .token(Token {
189    ///         access_token: String::from("YOUR_ACCESS_TOKEN"),
190    ///         refresh_token: String::from("YOUR_REFRESH_TOKEN"),
191    ///         id_token: String::from("YOUR_ID_TOKEN"),
192    ///         token_type: String::from("Bearer"),
193    ///         scope: String::from("YOUR_SCOPES SPACE_SEPERATED FOR_EACH_SCOPE"),
194    ///         expires_in: 1200,
195    ///     })?
196    ///     .build()
197    ///     .await?;
198    ///
199    /// // Grab your accounts and specify an account the orders were placed in
200    /// let accounts = client.get_accounts().await?;
201    /// if let Some(specific_account) = accounts.find_by_id("YOUR_ACCOUNT_ID") {
202    ///     // Get all the orders from today for a specific account
203    ///     let orders = specific_account.get_orders( &mut client).await?;
204    ///
205    ///     // Filter out only filled orders
206    ///     let filled_orders: Vec<Order> = orders
207    ///         .into_iter()
208    ///         .filter(|order| order.status == "FLL")
209    ///         .collect();
210    ///
211    ///     // Do something with your filled orders
212    ///     for order in filled_orders {
213    ///         println!("Filled Order: {order:?}");
214    ///     }
215    /// }
216    /// ```
217    pub async fn get_orders(&self, client: &mut Client) -> Result<Vec<Order>, Error> {
218        let endpoint = format!("brokerage/accounts/{}/orders", self.account_id);
219
220        let resp = client
221            .get(&endpoint)
222            .await?
223            .json::<responses::GetOrdersResp>()
224            .await?;
225
226        Ok(resp.orders)
227    }
228
229    /// NOTE: Same as `get_orders` but for multiple accounts
230    /// NOTE: For internal use only. Use `Account::get_orders_by_id()`
231    /// to access this functionality.
232    async fn get_orders_for_accounts<S: Into<String>>(
233        account_ids: Vec<S>,
234        client: &mut Client,
235    ) -> Result<Vec<Order>, Error> {
236        let account_ids: Vec<String> = account_ids
237            .into_iter()
238            .map(|account_id| account_id.into())
239            .collect();
240
241        let endpoint = format!("brokerage/accounts/{}/orders", account_ids.join(","));
242
243        let resp = client
244            .get(&endpoint)
245            .await?
246            .json::<responses::GetOrdersResp>()
247            .await?;
248
249        Ok(resp.orders)
250    }
251
252    /// Fetches orders by order id for the given `Account`.
253    ///
254    /// # Example
255    /// ---
256    ///
257    /// Grab 2 specific orders by their id's, say you have a stop loss order
258    /// and a take profit order you want to check the status on, this is how.
259    ///
260    /// ```ignore
261    /// // Initialize the client
262    /// let mut client = ClientBuilder::new()?
263    ///     .credentials("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")?
264    ///     .token(Token {
265    ///         access_token: String::from("YOUR_ACCESS_TOKEN"),
266    ///         refresh_token: String::from("YOUR_REFRESH_TOKEN"),
267    ///         id_token: String::from("YOUR_ID_TOKEN"),
268    ///         token_type: String::from("Bearer"),
269    ///         scope: String::from("YOUR_SCOPES SPACE_SEPERATED FOR_EACH_SCOPE"),
270    ///         expires_in: 1200,
271    ///     })?
272    ///     .build()
273    ///     .await?;
274    ///
275    /// // Grab your accounts and specify an account the orders were placed in
276    /// let accounts = client.get_accounts().await?;
277    /// if let Some(specific_account) = accounts.find_by_id("YOUR_ACCOUNT_ID") {
278    ///     // Get some specific orders by their order id's
279    ///     let orders = specific_account.
280    ///         get_orders_by_id(vec!["1115661503", "1115332365"], &mut client)
281    ///         .await?;
282    ///
283    ///     // Log the status of the order's
284    ///     for order in orders {
285    ///         println!("Order ID ({}) status: {}", order.order_id, order.status);
286    ///     }
287    /// }
288    /// ```
289    pub async fn get_orders_by_id<S: Into<String>>(
290        &self,
291        order_ids: Vec<S>,
292        client: &mut Client,
293    ) -> Result<Vec<Order>, Error> {
294        let order_ids: Vec<String> = order_ids.into_iter().map(|id| id.into()).collect();
295
296        let endpoint = format!(
297            "brokerage/accounts/{}/orders/{}",
298            self.account_id,
299            &order_ids.join(",")
300        );
301
302        let resp = client
303            .get(&endpoint)
304            .await?
305            .json::<responses::GetOrdersResp>()
306            .await?;
307
308        Ok(resp.orders)
309    }
310
311    /// NOTE: Same as `get_orders_by_id` but for multiple accounts
312    /// NOTE: For internal use only. Use `Account::get_orders_by_id()`
313    /// to access this functionality.
314    async fn get_orders_by_id_for_accounts<S: Into<String>>(
315        account_ids: Vec<S>,
316        order_ids: Vec<S>,
317        client: &mut Client,
318    ) -> Result<Vec<Order>, Error> {
319        let account_ids: Vec<String> = account_ids
320            .into_iter()
321            .map(|account_id| account_id.into())
322            .collect();
323
324        let order_ids: Vec<String> = order_ids
325            .into_iter()
326            .map(|order_id| order_id.into())
327            .collect();
328
329        let endpoint = format!(
330            "brokerage/accounts/{}/orders/{}",
331            account_ids.join(","),
332            &order_ids.join(",")
333        );
334
335        let resp = client
336            .get(&endpoint)
337            .await?
338            .json::<responses::GetOrdersResp>()
339            .await?;
340
341        Ok(resp.orders)
342    }
343
344    /// Fetches positions for the given `Account`.
345    pub async fn get_positions(&self, client: &mut Client) -> Result<Vec<Position>, Error> {
346        let endpoint = format!("brokerage/accounts/{}/positions", self.account_id);
347
348        let resp = client
349            .get(&endpoint)
350            .await?
351            .json::<responses::GetPositionsResp>()
352            .await?;
353
354        Ok(resp.positions)
355    }
356
357    /// Fetches positions for the given `Account`.
358    ///
359    /// NOTE: symbol should be a str of valid symbols in comma separated format;
360    /// for example: `"MSFT,MSFT *,AAPL"`.
361    ///
362    /// NOTE: You can use an * as wildcard to make more complex filters.
363    pub async fn get_positions_in_symbols(
364        &self,
365        symbols: &str,
366        client: &mut Client,
367    ) -> Result<Vec<Position>, Error> {
368        let endpoint = format!(
369            "brokerage/accounts/{}/positions?symbol={}",
370            self.account_id, symbols
371        );
372
373        let resp = client
374            .get(&endpoint)
375            .await?
376            .json::<responses::GetPositionsResp>()
377            .await?;
378
379        Ok(resp.positions)
380    }
381
382    /// Fetches positions for the given `Account`.
383    ///
384    /// NOTE: If you have `Vec<Account>` you should instead use `Vec<Account>::get_positions()`
385    /// this method should only be used if you ONLY have account id's.
386    pub async fn get_positions_by_ids(
387        client: &mut Client,
388        account_ids: Vec<&str>,
389    ) -> Result<Vec<Position>, Error> {
390        let endpoint = format!("brokerage/accounts/{}/positions", account_ids.join(","));
391
392        let resp = client
393            .get(&endpoint)
394            .await?
395            .json::<responses::GetPositionsResp>()
396            .await?;
397
398        Ok(resp.positions)
399    }
400
401    /// Fetches positions for the given `Account`.
402    ///
403    /// NOTE: If you have `Vec<Account>` you should instead use `Vec<Account>::get_positions_in_symbols()`
404    /// this method should only be used if you ONLY have account id's.
405    ///
406    /// NOTE: symbol should be a str of valid symbols in comma separated format;
407    /// for example: `"MSFT,MSFT *,AAPL"`.
408    ///
409    /// NOTE: You can use an * as wildcard to make more complex filters.
410    pub async fn get_positions_in_symbols_by_ids(
411        client: &mut Client,
412        symbols: &str,
413        account_ids: Vec<&str>,
414    ) -> Result<Vec<Position>, Error> {
415        let endpoint = format!(
416            "brokerage/accounts/{}/positions?symbol={}",
417            account_ids.join(","),
418            symbols
419        );
420
421        let resp = client
422            .get(&endpoint)
423            .await?
424            .json::<responses::GetPositionsResp>()
425            .await?;
426
427        Ok(resp.positions)
428    }
429
430    /// Stream `Order`(s) for the given `Account`.
431    ///
432    /// NOTE: You need to pass a closure function that will handle
433    /// each chunk of data (`StreamOrdersResp`) as it's streamed in.
434    ///
435    /// # Example
436    /// ---
437    ///
438    /// Get the amount of funds allocated to open orders.
439    /// ```ignore
440    /// let mut funds_allocated_to_open_orders = 0.00;
441    /// specific_account
442    ///     .stream_orders(&mut client, |stream_data| {
443    ///         // The response type is `responses::account::StreamOrdersResp`
444    ///         // which has multiple variants the main one you care about is
445    ///         // `Order` which will contain order data sent from the stream.
446    ///         match stream_data {
447    ///             StreamOrdersResp::Order(order) => {
448    ///                 // Response for an `Order` streamed in
449    ///                 println!("{order:?}");
450    ///
451    ///                 // keep a live sum of all the funds allocated to open orders
452    ///                 let order_value = order.price_used_for_buying_power.parse::<f64>();
453    ///                 if let Ok(value) = order_value {
454    ///                     funds_allocated_to_open_orders += value;
455    ///                 }
456    ///             }
457    ///             StreamOrdersResp::Heartbeat(heartbeat) => {
458    ///                 // Response for periodic signals letting you know the connection is
459    ///                 // still alive. A heartbeat is sent every 5 seconds of inactivity.
460    ///                 println!("{heartbeat:?}");
461    ///
462    ///                 // for the sake of this example after we recieve the
463    ///                 // tenth heartbeat, we will stop the stream session.
464    ///                 if heartbeat.heartbeat > 10 {
465    ///                     // Example: stopping a stream connection
466    ///                     return Err(Error::StopStream);
467    ///                 }
468    ///             }
469    ///             StreamOrdersResp::Status(status) => {
470    ///                 // Signal sent on state changes in the stream
471    ///                 // (closed, opened, paused, resumed)
472    ///                 println!("{status:?}");
473    ///             }
474    ///             StreamOrdersResp::Error(err) => {
475    ///                 // Response for when an error was encountered,
476    ///                 // with details on the error
477    ///                 println!("{err:?}");
478    ///             }
479    ///         }
480    ///
481    ///         Ok(())
482    ///     })
483    ///     .await?;
484    /// ```
485    pub async fn stream_orders<F>(
486        &self,
487        client: &mut Client,
488        mut on_chunk: F,
489    ) -> Result<Vec<Order>, Error>
490    where
491        F: FnMut(StreamOrdersResp) -> Result<(), Error>,
492    {
493        let endpoint = format!("brokerage/stream/accounts/{}/orders", self.account_id);
494
495        let mut collected_orders: Vec<Order> = Vec::new();
496        client
497            .stream(&endpoint, |chunk| {
498                let parsed_chunk = serde_json::from_value::<StreamOrdersResp>(chunk)?;
499                on_chunk(parsed_chunk.clone())?;
500
501                // Only collect orders, so when the stream is done
502                // all the orders that were streamed can be returned
503                if let StreamOrdersResp::Order(order) = parsed_chunk {
504                    collected_orders.push(*order);
505                }
506
507                Ok(())
508            })
509            .await?;
510
511        Ok(collected_orders)
512    }
513
514    /// Stream `Order`(s) by order id's for the given `Account`.
515    ///
516    /// NOTE: order ids should be a comma delimited string slice `"xxxxx,xxxxx,xxxxx"`
517    ///
518    /// NOTE: You need to pass a closure function that will handle
519    /// each chunk of data `StreamOrdersResp` as it's streamed in.
520    ///
521    /// # Example
522    /// ---
523    ///
524    /// Do something until all order's in a trade are filled.
525    ///
526    /// ```ignore
527    /// let mut some_trades_order_statuses: HashMap<String, String> = HashMap::new();
528    /// specific_account
529    ///     // NOTE: The order ids "1111,1112,1113,1114" are fake and not to be used.
530    ///     .stream_orders_by_id(&mut client, "1111,1112,1113,1114", |stream_data| {
531    ///         // The response type is `responses::account::StreamOrdersResp`
532    ///         // which has multiple variants the main one you care about is
533    ///         // `Order` which will contain order data sent from the stream.
534    ///         match stream_data {
535    ///             StreamOrdersResp::Order(order) => {
536    ///                 // Response for an `Order` streamed in
537    ///                 println!("{order:?}");
538    ///
539    ///                 some_trades_order_statuses.insert(order.order_id, order.status);
540    ///                 if some_trades_order_statuses
541    ///                     .values()
542    ///                     .all(|order_status| order_status.eq("FLL"))
543    ///                 {
544    ///                     // When all order's are filled stop the stream
545    ///                     return Err(Error::StopStream);
546    ///                 } else {
547    ///                     // Do something until all order's for a specific trade are filled
548    ///                     // maybe update the limit price of the unfilled order's by 1 tick?
549    ///                     //
550    ///                     // NOTE: you can also "do nothing" essentially just waiting for some
551    ///                     // scenario, maybe waiting for all order's to be filled to send an
552    ///                     // email or text alerting that the trade is fully filled.
553    ///                 }
554    ///             }
555    ///             StreamOrdersResp::Heartbeat(heartbeat) => {
556    ///                 // Response for periodic signals letting you know the connection is
557    ///                 // still alive. A heartbeat is sent every 5 seconds of inactivity.
558    ///                 println!("{heartbeat:?}");
559    ///
560    ///                 // for the sake of this example after we recieve the
561    ///                 // tenth heartbeat, we will stop the stream session.
562    ///                 if heartbeat.heartbeat > 10 {
563    ///                     // Example: stopping a stream connection
564    ///                     return Err(Error::StopStream);
565    ///                 }
566    ///             }
567    ///             StreamOrdersResp::Status(status) => {
568    ///                 // Signal sent on state changes in the stream
569    ///                 // (closed, opened, paused, resumed)
570    ///                 println!("{status:?}");
571    ///             }
572    ///             StreamOrdersResp::Error(err) => {
573    ///                 // Response for when an error was encountered,
574    ///                 // with details on the error
575    ///                 println!("{err:?}");
576    ///             }
577    ///         }
578    ///
579    ///         Ok(())
580    ///     })
581    ///     .await?;
582    /// ```
583    pub async fn stream_orders_by_id<F>(
584        &self,
585        client: &mut Client,
586        order_ids: &str,
587        mut on_chunk: F,
588    ) -> Result<Vec<Order>, Error>
589    where
590        F: FnMut(StreamOrdersResp) -> Result<(), Error>,
591    {
592        let endpoint = format!(
593            "brokerage/stream/accounts/{}/orders/{}",
594            self.account_id, order_ids
595        );
596
597        let mut collected_orders: Vec<Order> = Vec::new();
598        client
599            .stream(&endpoint, |chunk| {
600                let parsed_chunk = serde_json::from_value::<StreamOrdersResp>(chunk)?;
601                on_chunk(parsed_chunk.clone())?;
602
603                // Only collect orders, so when the stream is done
604                // all the orders that were streamed can be returned
605                if let StreamOrdersResp::Order(order) = parsed_chunk {
606                    collected_orders.push(*order);
607                }
608
609                Ok(())
610            })
611            .await?;
612
613        Ok(collected_orders)
614    }
615
616    /// Stream `Order`(s) for the given `Account`.
617    ///
618    /// NOTE: You need to pass a closure function that will handle
619    /// each chunk of data (`StreamOrdersResp`) as it's streamed in.
620    ///
621    /// # Example
622    /// ---
623    ///
624    /// Get the amount of funds allocated to open orders.
625    /// ```ignore
626    /// let mut funds_allocated_to_open_orders = 0.00;
627    /// specific_account
628    ///     .stream_orders(&mut client, |stream_data| {
629    ///         // The response type is `responses::account::StreamOrdersResp`
630    ///         // which has multiple variants the main one you care about is
631    ///         // `Order` which will contain order data sent from the stream.
632    ///         match stream_data {
633    ///             StreamOrdersResp::Order(order) => {
634    ///                 // Response for an `Order` streamed in
635    ///                 println!("{order:?}");
636    ///
637    ///                 // keep a live sum of all the funds allocated to open orders
638    ///                 let order_value = order.price_used_for_buying_power.parse::<f64>();
639    ///                 if let Ok(value) = order_value {
640    ///                     funds_allocated_to_open_orders += value;
641    ///                 }
642    ///             }
643    ///             StreamOrdersResp::Heartbeat(heartbeat) => {
644    ///                 // Response for periodic signals letting you know the connection is
645    ///                 // still alive. A heartbeat is sent every 5 seconds of inactivity.
646    ///                 println!("{heartbeat:?}");
647    ///
648    ///                 // for the sake of this example after we recieve the
649    ///                 // tenth heartbeat, we will stop the stream session.
650    ///                 if heartbeat.heartbeat > 10 {
651    ///                     // Example: stopping a stream connection
652    ///                     return Err(Error::StopStream);
653    ///                 }
654    ///             }
655    ///             StreamOrdersResp::Status(status) => {
656    ///                 // Signal sent on state changes in the stream
657    ///                 // (closed, opened, paused, resumed)
658    ///                 println!("{status:?}");
659    ///             }
660    ///             StreamOrdersResp::Error(err) => {
661    ///                 // Response for when an error was encountered,
662    ///                 // with details on the error
663    ///                 println!("{err:?}");
664    ///             }
665    ///         }
666    ///
667    ///         Ok(())
668    ///     })
669    ///     .await?;
670    /// ```
671    async fn stream_orders_for_accounts<F>(
672        client: &mut Client,
673        account_ids: Vec<&str>,
674        mut on_chunk: F,
675    ) -> Result<Vec<Order>, Error>
676    where
677        F: FnMut(StreamOrdersResp) -> Result<(), Error>,
678    {
679        let endpoint = format!("brokerage/stream/accounts/{}/orders", account_ids.join(","));
680
681        let mut collected_orders: Vec<Order> = Vec::new();
682        client
683            .stream(&endpoint, |chunk| {
684                let parsed_chunk = serde_json::from_value::<StreamOrdersResp>(chunk)?;
685                on_chunk(parsed_chunk.clone())?;
686
687                // Only collect orders so when the stream is done
688                // all the orders that were streamed can be returned
689                if let StreamOrdersResp::Order(order) = parsed_chunk {
690                    collected_orders.push(*order);
691                }
692
693                Ok(())
694            })
695            .await?;
696
697        Ok(collected_orders)
698    }
699
700    /// Stream `Order`s by order id's for the given `Account`(s).
701    ///
702    /// NOTE: order ids should be a comma delimited string slice `"xxxxx,xxxxx,xxxxx"`
703    ///
704    /// NOTE: You need to pass a closure function that will handle
705    /// each chunk of data (`StreamOrdersResp`) as it's streamed in.
706    ///
707    /// # Example
708    /// ---
709    ///
710    /// Do something until all order's in a trade are filled.
711    /// ```ignore
712    /// let mut some_trades_order_statuses: HashMap<String, String> = HashMap::new();
713    /// specific_account
714    ///     // NOTE: The order ids "1111,1112,1113,1114" are fake and not to be used.
715    ///     .stream_orders_by_id(&mut client, "1111,1112,1113,1114", |stream_data| {
716    ///         // The response type is `responses::account::StreamOrdersResp`
717    ///         // which has multiple variants the main one you care about is
718    ///         // `Order` which will contain order data sent from the stream.
719    ///         match stream_data {
720    ///             StreamOrdersResp::Order(order) => {
721    ///                 // Response for an `Order` streamed in
722    ///                 println!("{order:?}");
723    ///
724    ///                 some_trades_order_statuses.insert(order.order_id, order.status);
725    ///                 if some_trades_order_statuses
726    ///                     .values()
727    ///                     .all(|order_status| order_status.eq("FLL"))
728    ///                 {
729    ///                     // When all order's are filled stop the stream
730    ///                     return Err(Error::StopStream);
731    ///                 } else {
732    ///                     // Do something until all order's for a specific trade are filled
733    ///                     // maybe update the limit price of the unfilled order's by 1 tick?
734    ///                     //
735    ///                     // NOTE: you can also "do nothing" essentially just waiting for some
736    ///                     // scenario, maybe waiting for all order's to be filled to send an
737    ///                     // email or text alerting that the trade is fully filled.
738    ///                 }
739    ///             }
740    ///             StreamOrdersResp::Heartbeat(heartbeat) => {
741    ///                 // Response for periodic signals letting you know the connection is
742    ///                 // still alive. A heartbeat is sent every 5 seconds of inactivity.
743    ///                 println!("{heartbeat:?}");
744    ///
745    ///                 // for the sake of this example after we recieve the
746    ///                 // tenth heartbeat, we will stop the stream session.
747    ///                 if heartbeat.heartbeat > 10 {
748    ///                     // Example: stopping a stream connection
749    ///                     return Err(Error::StopStream);
750    ///                 }
751    ///             }
752    ///             StreamOrdersResp::Status(status) => {
753    ///                 // Signal sent on state changes in the stream
754    ///                 // (closed, opened, paused, resumed)
755    ///                 println!("{status:?}");
756    ///             }
757    ///             StreamOrdersResp::Error(err) => {
758    ///                 // Response for when an error was encountered,
759    ///                 // with details on the error
760    ///                 println!("{err:?}");
761    ///             }
762    ///         }
763    ///
764    ///         Ok(())
765    ///     })
766    ///     .await?;
767    /// ```
768    async fn stream_orders_by_id_for_accounts<F>(
769        client: &mut Client,
770        order_ids: &str,
771        account_ids: Vec<&str>,
772        mut on_chunk: F,
773    ) -> Result<Vec<Order>, Error>
774    where
775        F: FnMut(StreamOrdersResp) -> Result<(), Error>,
776    {
777        let endpoint = format!(
778            "brokerage/stream/accounts/{}/orders/{}",
779            account_ids.join(","),
780            order_ids
781        );
782
783        let mut collected_orders: Vec<Order> = Vec::new();
784        client
785            .stream(&endpoint, |chunk| {
786                let parsed_chunk = serde_json::from_value::<StreamOrdersResp>(chunk)?;
787                on_chunk(parsed_chunk.clone())?;
788
789                // Only collect orders so when the stream is done
790                // all the orders that were streamed can be returned
791                if let StreamOrdersResp::Order(order) = parsed_chunk {
792                    collected_orders.push(*order);
793                }
794
795                Ok(())
796            })
797            .await?;
798
799        Ok(collected_orders)
800    }
801
802    /// Stream `Position`s for the given `Account`.
803    ///
804    /// NOTE: TODO: Currently does NOT support streaming `Position` changes.
805    ///
806    /// # Example
807    /// ---
808    ///
809    /// Collect losing trades into a vector and do something with them.
810    /// ```ignore
811    /// let mut losing_positions: Vec<Position> = Vec::new();
812    /// specific_account
813    ///     .stream_positions(&mut client, |stream_data| {
814    ///         // the response type is `responses::account::StreamPositionsResp`
815    ///         // which has multiple variants the main one you care about is
816    ///         // `order` which will contain order data sent from the stream.
817    ///         match stream_data {
818    ///             StreamPositionsResp::Position(position) => {
819    ///                 // response for an `position` streamed in
820    ///                 println!("{position:?}");
821    ///
822    ///                 if position.long_short == PositionType::Long {
823    ///                     if position.last < position.average_price {
824    ///                         losing_positions.push(*position)
825    ///                     }
826    ///                 } else if position.long_short == PositionType::Short {
827    ///                     if position.last > position.average_price {
828    ///                         losing_positions.push(*position)
829    ///                     }
830    ///                 }
831    ///
832    ///                 // do something with the list of losing trades
833    ///                 // maybe send email or text of the positions
834    ///                 println!("{losing_positions:?}");
835    ///             }
836    ///             StreamPositionsResp::Heartbeat(heartbeat) => {
837    ///                 // response for periodic signals letting you know the connection is
838    ///                 // still alive. a heartbeat is sent every 5 seconds of inactivity.
839    ///                 println!("{heartbeat:?}");
840    ///
841    ///                 // for the sake of this example after we recieve the
842    ///                 // tenth heartbeat, we will stop the stream session.
843    ///                 if heartbeat.heartbeat > 10 {
844    ///                     // example: stopping a stream connection
845    ///                     return Err(Error::StopStream);
846    ///                 }
847    ///             }
848    ///             StreamPositionsResp::Status(status) => {
849    ///                 // signal sent on state changes in the stream
850    ///                 // (closed, opened, paused, resumed)
851    ///                 println!("{status:?}");
852    ///             }
853    ///             StreamPositionsResp::Error(err) => {
854    ///                 // response for when an error was encountered,
855    ///                 // with details on the error
856    ///                 println!("{err:?}");
857    ///             }
858    ///         }
859    ///
860    ///         Ok(())
861    ///     })
862    ///     .await?;
863    /// ```
864    pub async fn stream_positions<F>(
865        &self,
866        client: &mut Client,
867        mut on_chunk: F,
868    ) -> Result<Vec<Position>, Error>
869    where
870        F: FnMut(StreamPositionsResp) -> Result<(), Error>,
871    {
872        let endpoint = format!("brokerage/stream/accounts/{}/positions", self.account_id);
873
874        let mut collected_positions: Vec<Position> = Vec::new();
875        client
876            .stream(&endpoint, |chunk| {
877                let parsed_chunk = serde_json::from_value::<StreamPositionsResp>(chunk)?;
878                on_chunk(parsed_chunk.clone())?;
879
880                // Only collect orders, so when the stream is done
881                // all the orders that were streamed can be returned
882                if let StreamPositionsResp::Position(position) = parsed_chunk {
883                    collected_positions.push(*position);
884                }
885
886                Ok(())
887            })
888            .await?;
889
890        Ok(collected_positions)
891    }
892
893    /// Stream `Position`s for the given `Account`(s).
894    ///
895    /// NOTE: TODO: Currently does NOT support streaming `Position` changes.
896    ///
897    /// # Example
898    /// ---
899    ///
900    /// Collect losing trades into a vector and do something with them.
901    /// ```ignore
902    /// let mut losing_positions: Vec<Position> = Vec::new();
903    /// specific_account
904    ///     .stream_positions(&mut client, |stream_data| {
905    ///         // the response type is `responses::account::StreamPositionsResp`
906    ///         // which has multiple variants the main one you care about is
907    ///         // `order` which will contain order data sent from the stream.
908    ///         match stream_data {
909    ///             StreamPositionsResp::Position(position) => {
910    ///                 // response for an `position` streamed in
911    ///                 println!("{position:?}");
912    ///
913    ///                 if position.long_short == PositionType::Long {
914    ///                     if position.last < position.average_price {
915    ///                         losing_positions.push(*position)
916    ///                     }
917    ///                 } else if position.long_short == PositionType::Short {
918    ///                     if position.last > position.average_price {
919    ///                         losing_positions.push(*position)
920    ///                     }
921    ///                 }
922    ///
923    ///                 // do something with the list of losing trades
924    ///                 // maybe send email or text of the positions
925    ///                 println!("{losing_positions:?}");
926    ///             }
927    ///             StreamPositionsResp::Heartbeat(heartbeat) => {
928    ///                 // response for periodic signals letting you know the connection is
929    ///                 // still alive. a heartbeat is sent every 5 seconds of inactivity.
930    ///                 println!("{heartbeat:?}");
931    ///
932    ///                 // for the sake of this example after we recieve the
933    ///                 // tenth heartbeat, we will stop the stream session.
934    ///                 if heartbeat.heartbeat > 10 {
935    ///                     // example: stopping a stream connection
936    ///                     return Err(Error::StopStream);
937    ///                 }
938    ///             }
939    ///             StreamPositionsResp::Status(status) => {
940    ///                 // signal sent on state changes in the stream
941    ///                 // (closed, opened, paused, resumed)
942    ///                 println!("{status:?}");
943    ///             }
944    ///             StreamPositionsResp::Error(err) => {
945    ///                 // response for when an error was encountered,
946    ///                 // with details on the error
947    ///                 println!("{err:?}");
948    ///             }
949    ///         }
950    ///
951    ///         Ok(())
952    ///     })
953    ///     .await?;
954    /// ```
955    pub async fn stream_positions_for_accounts<F>(
956        client: &mut Client,
957        account_ids: Vec<&str>,
958        mut on_chunk: F,
959    ) -> Result<Vec<Position>, Error>
960    where
961        F: FnMut(StreamPositionsResp) -> Result<(), Error>,
962    {
963        let endpoint = format!(
964            "brokerage/stream/accounts/{}/positions",
965            account_ids.join(",")
966        );
967
968        let mut collected_positions: Vec<Position> = Vec::new();
969        client
970            .stream(&endpoint, |chunk| {
971                let parsed_chunk = serde_json::from_value::<StreamPositionsResp>(chunk)?;
972                on_chunk(parsed_chunk.clone())?;
973
974                // Only collect orders, so when the stream is done
975                // all the orders that were streamed can be returned
976                if let StreamPositionsResp::Position(position) = parsed_chunk {
977                    collected_positions.push(*position);
978                }
979
980                Ok(())
981            })
982            .await?;
983
984        Ok(collected_positions)
985    }
986}
987
988/// Trait to allow calling methods on multiple accounts `Vec<Account>`.
989pub trait MultipleAccounts {
990    /// Find an `Account` by it's id.
991    fn find_by_id(&self, id: &str) -> Option<Account>;
992
993    type GetOrdersFuture<'a>: Future<Output = Result<Vec<Order>, Box<dyn StdErrorTrait + Send + Sync>>>
994        + Send
995        + 'a
996    where
997        Self: 'a;
998    /// Get `Order`(s) for multiple `Account`(s).
999    ///
1000    /// # Example
1001    /// ---
1002    ///
1003    /// Grab all the orders for a specific account. Say you need to go
1004    /// through all the orders your algorithm placed today and filter out
1005    /// only the orders that were filled for data storage purposes.
1006    ///
1007    /// ```ignore
1008    /// // Initialize the client
1009    /// let mut client = ClientBuilder::new()?
1010    ///     .credentials("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")?
1011    ///     .token(Token {
1012    ///         access_token: String::from("YOUR_ACCESS_TOKEN"),
1013    ///         refresh_token: String::from("YOUR_REFRESH_TOKEN"),
1014    ///         id_token: String::from("YOUR_ID_TOKEN"),
1015    ///         token_type: String::from("Bearer"),
1016    ///         scope: String::from("YOUR_SCOPES SPACE_SEPERATED FOR_EACH_SCOPE"),
1017    ///         expires_in: 1200,
1018    ///     })?
1019    ///     .build()
1020    ///     .await?;
1021    ///
1022    /// // Grab your accounts and specify an account the orders were placed in
1023    /// let accounts = client.get_accounts().await?;
1024    /// if let Some(specific_account) = accounts.find_by_id("YOUR_ACCOUNT_ID") {
1025    ///     // Get all the orders from today for a specific account
1026    ///     let orders = specific_account.get_orders( &mut client).await?;
1027    ///
1028    ///     // Filter out only filled orders
1029    ///     let filled_orders: Vec<Order> = orders
1030    ///         .into_iter()
1031    ///         .filter(|order| order.status == "FLL")
1032    ///         .collect();
1033    ///
1034    ///     // Do something with your filled orders
1035    ///     for order in filled_orders {
1036    ///         println!("Filled Order: {order:?}");
1037    ///     }
1038    /// }
1039    /// ```
1040    fn get_orders<'a>(&'a self, client: &'a mut Client) -> Self::GetOrdersFuture<'a>;
1041
1042    /// Get specific `Order`(s) by their id's for multiple `Account`(s).
1043    ///
1044    /// # Example
1045    /// ---
1046    ///
1047    /// Grab 2 specific orders by their id's, say you have a stop loss order
1048    /// and a take profit order you want to check the status on, this is how.
1049    ///
1050    /// ```ignore
1051    /// // Initialize the client
1052    /// let mut client = ClientBuilder::new()?
1053    ///     .credentials("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")?
1054    ///     .token(Token {
1055    ///         access_token: String::from("YOUR_ACCESS_TOKEN"),
1056    ///         refresh_token: String::from("YOUR_REFRESH_TOKEN"),
1057    ///         id_token: String::from("YOUR_ID_TOKEN"),
1058    ///         token_type: String::from("Bearer"),
1059    ///         scope: String::from("YOUR_SCOPES SPACE_SEPERATED FOR_EACH_SCOPE"),
1060    ///         expires_in: 1200,
1061    ///     })?
1062    ///     .build()
1063    ///     .await?;
1064    ///
1065    /// // Grab your accounts and specify an account the orders were placed in
1066    /// let accounts = client.get_accounts().await?;
1067    /// if let Some(specific_account) = accounts.find_by_id("YOUR_ACCOUNT_ID") {
1068    ///     // Get some specific orders by their order id's
1069    ///     let orders = specific_account.
1070    ///         get_orders_by_id(vec!["1115661503", "1115332365"], &mut client)
1071    ///         .await?;
1072    ///
1073    ///     // Log the status of the order's
1074    ///     for order in orders {
1075    ///         println!("Order ID ({}) status: {}", order.order_id, order.status);
1076    ///     }
1077    /// }
1078    /// ```
1079    fn get_orders_by_id<'a>(
1080        &'a self,
1081        order_ids: &'a [&str],
1082        client: &'a mut Client,
1083    ) -> Self::GetOrdersFuture<'a>;
1084
1085    type GetBalanceFuture<'a>: Future<Output = Result<Vec<Balance>, Box<dyn StdErrorTrait + Send + Sync>>>
1086        + Send
1087        + 'a
1088    where
1089        Self: 'a;
1090    /// Get the current balance of multiple `Account`(s).
1091    fn get_balances<'a>(&'a self, client: &'a mut Client) -> Self::GetBalanceFuture<'a>;
1092
1093    type GetBODBalanceFuture<'a>: Future<Output = Result<Vec<BODBalance>, Box<dyn StdErrorTrait + Send + Sync>>>
1094        + Send
1095        + 'a
1096    where
1097        Self: 'a;
1098    /// Get the beginning of day balances for multiple `Account`(s) by account id.
1099    fn get_bod_balances<'a>(&'a self, client: &'a mut Client) -> Self::GetBODBalanceFuture<'a>;
1100
1101    type GetHistoricOrdersFuture<'a>: Future<Output = Result<Vec<Order>, Box<dyn StdErrorTrait + Send + Sync>>>
1102        + Send
1103        + 'a
1104    where
1105        Self: 'a;
1106    /// Get the historical `Order`(s) for multiple `Account`(s).
1107    ///
1108    /// NOTE: Date format is {YEAR-MONTH-DAY} ex: `"2024-07-09"`, and is limited to 90
1109    /// days prior to the current date.
1110    ///
1111    /// NOTE: Excludes open `Order`(s) and is sorted in descending order of time closed.
1112    fn get_historic_orders<'a>(
1113        &'a self,
1114        client: &'a mut Client,
1115        since_date: &'a str,
1116    ) -> Self::GetHistoricOrdersFuture<'a>;
1117
1118    type GetPositionsFuture<'a>: Future<Output = Result<Vec<Position>, Box<dyn StdErrorTrait + Send + Sync>>>
1119        + Send
1120        + 'a
1121    where
1122        Self: 'a;
1123    /// Get the `Position`(s) for multiple `Account`(s).
1124    fn get_positions<'a>(&'a self, client: &'a mut Client) -> Self::GetPositionsFuture<'a>;
1125
1126    type GetPositionsInSymbolsFuture<'a>: Future<Output = Result<Vec<Position>, Box<dyn StdErrorTrait + Send + Sync>>>
1127        + Send
1128        + 'a
1129    where
1130        Self: 'a;
1131    /// Get the `Position`(s) in specific symbols for multiple `Account`(s).
1132    fn get_positions_in_symbols<'a>(
1133        &'a self,
1134        symbols: &'a str,
1135        client: &'a mut Client,
1136    ) -> Self::GetPositionsFuture<'a>;
1137
1138    type StreamOrdersFuture<'a>: Future<Output = Result<Vec<Order>, Box<dyn StdErrorTrait + Send + Sync>>>
1139        + Send
1140        + 'a
1141    where
1142        Self: 'a;
1143    /// Stream `Order`(s) for the given `Account`.
1144    ///
1145    /// NOTE: You need to pass a closure function that will handle
1146    /// each chunk of data (`StreamOrdersResp`) as it's streamed in.
1147    ///
1148    /// # Example
1149    /// ---
1150    ///
1151    /// Get the amount of funds allocated to open orders.
1152    /// ```ignore
1153    /// let mut funds_allocated_to_open_orders = 0.00;
1154    /// specific_account
1155    ///     .stream_orders(&mut client, |stream_data| {
1156    ///         // The response type is `responses::account::StreamOrdersResp`
1157    ///         // which has multiple variants the main one you care about is
1158    ///         // `Order` which will contain order data sent from the stream.
1159    ///         match stream_data {
1160    ///             StreamOrdersResp::Order(order) => {
1161    ///                 // Response for an `Order` streamed in
1162    ///                 println!("{order:?}");
1163    ///
1164    ///                 // keep a live sum of all the funds allocated to open orders
1165    ///                 let order_value = order.price_used_for_buying_power.parse::<f64>();
1166    ///                 if let Ok(value) = order_value {
1167    ///                     funds_allocated_to_open_orders += value;
1168    ///                 }
1169    ///             }
1170    ///             StreamOrdersResp::Heartbeat(heartbeat) => {
1171    ///                 // Response for periodic signals letting you know the connection is
1172    ///                 // still alive. A heartbeat is sent every 5 seconds of inactivity.
1173    ///                 println!("{heartbeat:?}");
1174    ///
1175    ///                 // for the sake of this example after we recieve the
1176    ///                 // tenth heartbeat, we will stop the stream session.
1177    ///                 if heartbeat.heartbeat > 10 {
1178    ///                     // Example: stopping a stream connection
1179    ///                     return Err(Error::StopStream);
1180    ///                 }
1181    ///             }
1182    ///             StreamOrdersResp::Status(status) => {
1183    ///                 // Signal sent on state changes in the stream
1184    ///                 // (closed, opened, paused, resumed)
1185    ///                 println!("{status:?}");
1186    ///             }
1187    ///             StreamOrdersResp::Error(err) => {
1188    ///                 // Response for when an error was encountered,
1189    ///                 // with details on the error
1190    ///                 println!("{err:?}");
1191    ///             }
1192    ///         }
1193    ///
1194    ///         Ok(())
1195    ///     })
1196    ///     .await?;
1197    /// ```
1198    fn stream_orders<'a, F>(
1199        &'a self,
1200        on_chunk: &'a mut F,
1201        client: &'a mut Client,
1202    ) -> Self::StreamOrdersFuture<'a>
1203    where
1204        F: FnMut(StreamOrdersResp) -> Result<(), Error> + Send + 'a;
1205
1206    type StreamOrdersByIdFuture<'a>: Future<Output = Result<Vec<Order>, Box<dyn StdErrorTrait + Send + Sync>>>
1207        + Send
1208        + 'a
1209    where
1210        Self: 'a;
1211    /// Stream `Order`s by order id's for the given `Account`(s).
1212    ///
1213    /// NOTE: order ids should be a comma delimited string slice `"xxxxx,xxxxx,xxxxx"`.
1214    ///
1215    /// NOTE: You need to pass a closure function that will handle
1216    /// each chunk of data (`StreamOrdersResp`) as it's streamed in.
1217    ///
1218    /// # Example
1219    /// ---
1220    ///
1221    /// Do something until all order's in a trade are filled.
1222    /// ```ignore
1223    /// let mut some_trades_order_statuses: HashMap<String, String> = HashMap::new();
1224    /// specific_account
1225    ///     // NOTE: The order ids "1111,1112,1113,1114" are fake and not to be used.
1226    ///     .stream_orders_by_id(&mut client, "1111,1112,1113,1114", |stream_data| {
1227    ///         // The response type is `responses::account::StreamOrdersResp`
1228    ///         // which has multiple variants the main one you care about is
1229    ///         // `Order` which will contain order data sent from the stream.
1230    ///         match stream_data {
1231    ///             StreamOrdersResp::Order(order) => {
1232    ///                 // Response for an `Order` streamed in
1233    ///                 println!("{order:?}");
1234    ///
1235    ///                 some_trades_order_statuses.insert(order.order_id, order.status);
1236    ///                 if some_trades_order_statuses
1237    ///                     .values()
1238    ///                     .all(|order_status| order_status.eq("FLL"))
1239    ///                 {
1240    ///                     // When all order's are filled stop the stream
1241    ///                     return Err(Error::StopStream);
1242    ///                 } else {
1243    ///                     // Do something until all order's for a specific trade are filled
1244    ///                     // maybe update the limit price of the unfilled order's by 1 tick?
1245    ///                     //
1246    ///                     // NOTE: you can also "do nothing" essentially just waiting for some
1247    ///                     // scenario, maybe waiting for all order's to be filled to send an
1248    ///                     // email or text alerting that the trade is fully filled.
1249    ///                 }
1250    ///             }
1251    ///             StreamOrdersResp::Heartbeat(heartbeat) => {
1252    ///                 // Response for periodic signals letting you know the connection is
1253    ///                 // still alive. A heartbeat is sent every 5 seconds of inactivity.
1254    ///                 println!("{heartbeat:?}");
1255    ///
1256    ///                 // for the sake of this example after we recieve the
1257    ///                 // tenth heartbeat, we will stop the stream session.
1258    ///                 if heartbeat.heartbeat > 10 {
1259    ///                     // Example: stopping a stream connection
1260    ///                     return Err(Error::StopStream);
1261    ///                 }
1262    ///             }
1263    ///             StreamOrdersResp::Status(status) => {
1264    ///                 // Signal sent on state changes in the stream
1265    ///                 // (closed, opened, paused, resumed)
1266    ///                 println!("{status:?}");
1267    ///             }
1268    ///             StreamOrdersResp::Error(err) => {
1269    ///                 // Response for when an error was encountered,
1270    ///                 // with details on the error
1271    ///                 println!("{err:?}");
1272    ///             }
1273    ///         }
1274    ///
1275    ///         Ok(())
1276    ///     })
1277    ///     .await?;
1278    /// ```
1279    fn stream_orders_by_id<'a, F>(
1280        &'a self,
1281        order_ids: &'a str,
1282        on_chunk: &'a mut F,
1283        client: &'a mut Client,
1284    ) -> Self::StreamOrdersByIdFuture<'a>
1285    where
1286        F: FnMut(StreamOrdersResp) -> Result<(), Error> + Send + 'a;
1287
1288    type StreamPositionsFuture<'a>: Future<Output = Result<Vec<Position>, Box<dyn StdErrorTrait + Send + Sync>>>
1289        + Send
1290        + 'a
1291    where
1292        Self: 'a;
1293    /// Stream `Position`s for the given `Account`(s).
1294    ///
1295    /// NOTE: TODO: Currently does NOT support streaming `Position` changes.
1296    ///
1297    /// # Example
1298    /// ---
1299    ///
1300    /// Collect losing trades into a vector and do something with them.
1301    /// ```ignore
1302    /// let mut losing_positions: Vec<Position> = Vec::new();
1303    /// specific_account
1304    ///     .stream_positions(&mut client, |stream_data| {
1305    ///         // the response type is `responses::account::StreamPositionsResp`
1306    ///         // which has multiple variants the main one you care about is
1307    ///         // `order` which will contain order data sent from the stream.
1308    ///         match stream_data {
1309    ///             StreamPositionsResp::Position(position) => {
1310    ///                 // response for an `position` streamed in
1311    ///                 println!("{position:?}");
1312    ///
1313    ///                 if position.long_short == PositionType::Long {
1314    ///                     if position.last < position.average_price {
1315    ///                         losing_positions.push(*position)
1316    ///                     }
1317    ///                 } else if position.long_short == PositionType::Short {
1318    ///                     if position.last > position.average_price {
1319    ///                         losing_positions.push(*position)
1320    ///                     }
1321    ///                 }
1322    ///
1323    ///                 // do something with the list of losing trades
1324    ///                 // maybe send email or text of the positions
1325    ///                 println!("{losing_positions:?}");
1326    ///             }
1327    ///             StreamPositionsResp::Heartbeat(heartbeat) => {
1328    ///                 // response for periodic signals letting you know the connection is
1329    ///                 // still alive. a heartbeat is sent every 5 seconds of inactivity.
1330    ///                 println!("{heartbeat:?}");
1331    ///
1332    ///                 // for the sake of this example after we recieve the
1333    ///                 // tenth heartbeat, we will stop the stream session.
1334    ///                 if heartbeat.heartbeat > 10 {
1335    ///                     // example: stopping a stream connection
1336    ///                     return Err(Error::StopStream);
1337    ///                 }
1338    ///             }
1339    ///             StreamPositionsResp::Status(status) => {
1340    ///                 // signal sent on state changes in the stream
1341    ///                 // (closed, opened, paused, resumed)
1342    ///                 println!("{status:?}");
1343    ///             }
1344    ///             StreamPositionsResp::Error(err) => {
1345    ///                 // response for when an error was encountered,
1346    ///                 // with details on the error
1347    ///                 println!("{err:?}");
1348    ///             }
1349    ///         }
1350    ///
1351    ///         Ok(())
1352    ///     })
1353    ///     .await?;
1354    /// ```
1355    fn stream_positions<'a, F>(
1356        &'a self,
1357        on_chunk: &'a mut F,
1358        client: &'a mut Client,
1359    ) -> Self::StreamPositionsFuture<'a>
1360    where
1361        F: FnMut(StreamPositionsResp) -> Result<(), Error> + Send + 'a;
1362}
1363impl MultipleAccounts for Vec<Account> {
1364    /// Find a specific account by a given account id from
1365    /// a `Vec<Account>`.
1366    fn find_by_id(&self, id: &str) -> Option<Account> {
1367        self.iter()
1368            .filter(|account| account.account_id == id)
1369            .collect::<Vec<&Account>>()
1370            .pop()
1371            .cloned()
1372    }
1373
1374    type GetOrdersFuture<'a> = Pin<
1375        Box<
1376            dyn Future<Output = Result<Vec<Order>, Box<dyn StdErrorTrait + Send + Sync>>>
1377                + Send
1378                + 'a,
1379        >,
1380    >;
1381    fn get_orders<'a>(&'a self, client: &'a mut Client) -> Self::GetOrdersFuture<'a> {
1382        let account_ids: Vec<&str> = self
1383            .iter()
1384            .map(|account| account.account_id.as_str())
1385            .collect();
1386
1387        Box::pin(async move {
1388            let orders = Account::get_orders_for_accounts(account_ids, client).await?;
1389            Ok(orders)
1390        })
1391    }
1392
1393    fn get_orders_by_id<'a>(
1394        &'a self,
1395        order_ids: &'a [&str],
1396        client: &'a mut Client,
1397    ) -> Self::GetOrdersFuture<'a> {
1398        let account_ids: Vec<&str> = self
1399            .iter()
1400            .map(|account| account.account_id.as_str())
1401            .collect();
1402
1403        Box::pin(async move {
1404            let orders =
1405                Account::get_orders_by_id_for_accounts(account_ids, order_ids.to_vec(), client)
1406                    .await?;
1407
1408            Ok(orders)
1409        })
1410    }
1411
1412    type GetBalanceFuture<'a> = Pin<
1413        Box<
1414            dyn Future<Output = Result<Vec<Balance>, Box<dyn StdErrorTrait + Send + Sync>>>
1415                + Send
1416                + 'a,
1417        >,
1418    >;
1419    /// Get the beginning of day balances for multiple `Account`(s).
1420    fn get_balances<'a>(&'a self, client: &'a mut Client) -> Self::GetBalanceFuture<'a> {
1421        let account_ids: Vec<&str> = self
1422            .iter()
1423            .map(|account| account.account_id.as_str())
1424            .collect();
1425
1426        Box::pin(async move {
1427            let balances = Account::get_balances_by_ids(client, account_ids).await?;
1428            Ok(balances)
1429        })
1430    }
1431
1432    type GetBODBalanceFuture<'a> = Pin<
1433        Box<
1434            dyn Future<Output = Result<Vec<BODBalance>, Box<dyn StdErrorTrait + Send + Sync>>>
1435                + Send
1436                + 'a,
1437        >,
1438    >;
1439    /// Get the beginning of day balances for multiple `Account`(s)
1440    fn get_bod_balances<'a>(&'a self, client: &'a mut Client) -> Self::GetBODBalanceFuture<'a> {
1441        let account_ids: Vec<&str> = self
1442            .iter()
1443            .map(|account| account.account_id.as_str())
1444            .collect();
1445
1446        Box::pin(async move {
1447            let balances = Account::get_bod_balances_by_ids(client, account_ids).await?;
1448            Ok(balances)
1449        })
1450    }
1451
1452    type GetHistoricOrdersFuture<'a> = Pin<
1453        Box<
1454            dyn Future<Output = Result<Vec<Order>, Box<dyn StdErrorTrait + Send + Sync>>>
1455                + Send
1456                + 'a,
1457        >,
1458    >;
1459    /// Get the historical `Order`(s) for multiple `Account`(s).
1460    fn get_historic_orders<'a>(
1461        &'a self,
1462        client: &'a mut Client,
1463        since_date: &'a str,
1464    ) -> Self::GetHistoricOrdersFuture<'a> {
1465        let account_ids: Vec<&str> = self
1466            .iter()
1467            .map(|account| account.account_id.as_str())
1468            .collect();
1469
1470        Box::pin(async move {
1471            let balances =
1472                Account::get_historic_orders_by_ids(client, account_ids, since_date).await?;
1473            Ok(balances)
1474        })
1475    }
1476
1477    type GetPositionsFuture<'a> = Pin<
1478        Box<
1479            dyn Future<Output = Result<Vec<Position>, Box<dyn StdErrorTrait + Send + Sync>>>
1480                + Send
1481                + 'a,
1482        >,
1483    >;
1484    /// Get the `Position`(s) for multiple `Account`(s).
1485    fn get_positions<'a>(&'a self, client: &'a mut Client) -> Self::GetPositionsFuture<'a> {
1486        let account_ids: Vec<&str> = self
1487            .iter()
1488            .map(|account| account.account_id.as_str())
1489            .collect();
1490
1491        Box::pin(async move {
1492            let positions = Account::get_positions_by_ids(client, account_ids).await?;
1493            Ok(positions)
1494        })
1495    }
1496
1497    type GetPositionsInSymbolsFuture<'a> = Pin<
1498        Box<
1499            dyn Future<Output = Result<Vec<Position>, Box<dyn StdErrorTrait + Send + Sync>>>
1500                + Send
1501                + 'a,
1502        >,
1503    >;
1504    /// Get the `Position`(s) in specific symbols for multiple `Account`(s).
1505    fn get_positions_in_symbols<'a>(
1506        &'a self,
1507        symbols: &'a str,
1508        client: &'a mut Client,
1509    ) -> Self::GetPositionsFuture<'a> {
1510        let account_ids: Vec<&str> = self
1511            .iter()
1512            .map(|account| account.account_id.as_str())
1513            .collect();
1514
1515        Box::pin(async move {
1516            let positions =
1517                Account::get_positions_in_symbols_by_ids(client, symbols, account_ids).await?;
1518            Ok(positions)
1519        })
1520    }
1521
1522    type StreamOrdersFuture<'a> = Pin<
1523        Box<
1524            dyn Future<Output = Result<Vec<Order>, Box<dyn StdErrorTrait + Send + Sync>>>
1525                + Send
1526                + 'a,
1527        >,
1528    >;
1529    /// Stream `Order`(s) for the given `Account`
1530    ///
1531    /// NOTE: You need to pass a closure function that will handle
1532    /// each chunk of data (`StreamOrdersResp`) as it's streamed in.
1533    ///
1534    /// # Example
1535    /// ---
1536    ///
1537    /// Get the amount of funds allocated to open orders.
1538    /// ```ignore
1539    /// let mut funds_allocated_to_open_orders = 0.00;
1540    /// specific_account
1541    ///     .stream_orders(&mut client, |stream_data| {
1542    ///         // The response type is `responses::account::StreamOrdersResp`
1543    ///         // which has multiple variants the main one you care about is
1544    ///         // `Order` which will contain order data sent from the stream.
1545    ///         match stream_data {
1546    ///             StreamOrdersResp::Order(order) => {
1547    ///                 // Response for an `Order` streamed in
1548    ///                 println!("{order:?}");
1549    ///
1550    ///                 // keep a live sum of all the funds allocated to open orders
1551    ///                 let order_value = order.price_used_for_buying_power.parse::<f64>();
1552    ///                 if let Ok(value) = order_value {
1553    ///                     funds_allocated_to_open_orders += value;
1554    ///                 }
1555    ///             }
1556    ///             StreamOrdersResp::Heartbeat(heartbeat) => {
1557    ///                 // Response for periodic signals letting you know the connection is
1558    ///                 // still alive. A heartbeat is sent every 5 seconds of inactivity.
1559    ///                 println!("{heartbeat:?}");
1560    ///
1561    ///                 // for the sake of this example after we recieve the
1562    ///                 // tenth heartbeat, we will stop the stream session.
1563    ///                 if heartbeat.heartbeat > 10 {
1564    ///                     // Example: stopping a stream connection
1565    ///                     return Err(Error::StopStream);
1566    ///                 }
1567    ///             }
1568    ///             StreamOrdersResp::Status(status) => {
1569    ///                 // Signal sent on state changes in the stream
1570    ///                 // (closed, opened, paused, resumed)
1571    ///                 println!("{status:?}");
1572    ///             }
1573    ///             StreamOrdersResp::Error(err) => {
1574    ///                 // Response for when an error was encountered,
1575    ///                 // with details on the error
1576    ///                 println!("{err:?}");
1577    ///             }
1578    ///         }
1579    ///
1580    ///         Ok(())
1581    ///     })
1582    ///     .await?;
1583    /// ```
1584    fn stream_orders<'a, F>(
1585        &'a self,
1586        mut on_chunk: &'a mut F,
1587        client: &'a mut Client,
1588    ) -> Self::StreamOrdersFuture<'a>
1589    where
1590        F: FnMut(StreamOrdersResp) -> Result<(), Error> + Send + 'a,
1591    {
1592        let account_ids: Vec<&str> = self
1593            .iter()
1594            .map(|account| account.account_id.as_str())
1595            .collect();
1596
1597        Box::pin(async move {
1598            let orders =
1599                Account::stream_orders_for_accounts(client, account_ids, &mut on_chunk).await?;
1600            Ok(orders)
1601        })
1602    }
1603
1604    type StreamOrdersByIdFuture<'a> = Pin<
1605        Box<
1606            dyn Future<Output = Result<Vec<Order>, Box<dyn StdErrorTrait + Send + Sync>>>
1607                + Send
1608                + 'a,
1609        >,
1610    >;
1611    /// Stream `Order`s by order id's for the given `Account`(s)
1612    ///
1613    /// NOTE: order ids should be a comma delimited string slice `"xxxxx,xxxxx,xxxxx"`
1614    ///
1615    /// NOTE: You need to pass a closure function that will handle
1616    /// each chunk of data (`StreamOrdersResp`) as it's streamed in.
1617    ///
1618    /// # Example
1619    /// ---
1620    ///
1621    /// Do something until all order's in a trade are filled.
1622    /// ```ignore
1623    /// let mut some_trades_order_statuses: HashMap<String, String> = HashMap::new();
1624    /// specific_account
1625    ///     // NOTE: The order ids "1111,1112,1113,1114" are fake and not to be used.
1626    ///     .stream_orders_by_id(&mut client, "1111,1112,1113,1114", |stream_data| {
1627    ///         // The response type is `responses::account::StreamOrdersResp`
1628    ///         // which has multiple variants the main one you care about is
1629    ///         // `Order` which will contain order data sent from the stream.
1630    ///         match stream_data {
1631    ///             StreamOrdersResp::Order(order) => {
1632    ///                 // Response for an `Order` streamed in
1633    ///                 println!("{order:?}");
1634    ///
1635    ///                 some_trades_order_statuses.insert(order.order_id, order.status);
1636    ///                 if some_trades_order_statuses
1637    ///                     .values()
1638    ///                     .all(|order_status| order_status.eq("FLL"))
1639    ///                 {
1640    ///                     // When all order's are filled stop the stream
1641    ///                     return Err(Error::StopStream);
1642    ///                 } else {
1643    ///                     // Do something until all order's for a specific trade are filled
1644    ///                     // maybe update the limit price of the unfilled order's by 1 tick?
1645    ///                     //
1646    ///                     // NOTE: you can also "do nothing" essentially just waiting for some
1647    ///                     // scenario, maybe waiting for all order's to be filled to send an
1648    ///                     // email or text alerting that the trade is fully filled.
1649    ///                 }
1650    ///             }
1651    ///             StreamOrdersResp::Heartbeat(heartbeat) => {
1652    ///                 // Response for periodic signals letting you know the connection is
1653    ///                 // still alive. A heartbeat is sent every 5 seconds of inactivity.
1654    ///                 println!("{heartbeat:?}");
1655    ///
1656    ///                 // for the sake of this example after we recieve the
1657    ///                 // tenth heartbeat, we will stop the stream session.
1658    ///                 if heartbeat.heartbeat > 10 {
1659    ///                     // Example: stopping a stream connection
1660    ///                     return Err(Error::StopStream);
1661    ///                 }
1662    ///             }
1663    ///             StreamOrdersResp::Status(status) => {
1664    ///                 // Signal sent on state changes in the stream
1665    ///                 // (closed, opened, paused, resumed)
1666    ///                 println!("{status:?}");
1667    ///             }
1668    ///             StreamOrdersResp::Error(err) => {
1669    ///                 // Response for when an error was encountered,
1670    ///                 // with details on the error
1671    ///                 println!("{err:?}");
1672    ///             }
1673    ///         }
1674    ///
1675    ///         Ok(())
1676    ///     })
1677    ///     .await?;
1678    /// ```
1679    fn stream_orders_by_id<'a, F>(
1680        &'a self,
1681        order_ids: &'a str,
1682        mut on_chunk: &'a mut F,
1683        client: &'a mut Client,
1684    ) -> Self::StreamOrdersByIdFuture<'a>
1685    where
1686        F: FnMut(StreamOrdersResp) -> Result<(), Error> + Send + 'a,
1687    {
1688        let account_ids: Vec<&str> = self
1689            .iter()
1690            .map(|account| account.account_id.as_str())
1691            .collect();
1692
1693        Box::pin(async move {
1694            let orders = Account::stream_orders_by_id_for_accounts(
1695                client,
1696                order_ids,
1697                account_ids,
1698                &mut on_chunk,
1699            )
1700            .await?;
1701            Ok(orders)
1702        })
1703    }
1704
1705    type StreamPositionsFuture<'a> = Pin<
1706        Box<
1707            dyn Future<Output = Result<Vec<Position>, Box<dyn StdErrorTrait + Send + Sync>>>
1708                + Send
1709                + 'a,
1710        >,
1711    >;
1712    /// Stream `Position`s for the given `Account`(s).
1713    ///
1714    /// NOTE: TODO: Currently does NOT support streaming `Position` changes.
1715    ///
1716    /// # Example
1717    /// ---
1718    ///
1719    /// Collect losing trades into a vector and do something with them.
1720    /// ```ignore
1721    /// let mut losing_positions: Vec<Position> = Vec::new();
1722    /// specific_account
1723    ///     .stream_positions(&mut client, |stream_data| {
1724    ///         // the response type is `responses::account::StreamPositionsResp`
1725    ///         // which has multiple variants the main one you care about is
1726    ///         // `order` which will contain order data sent from the stream.
1727    ///         match stream_data {
1728    ///             StreamPositionsResp::Position(position) => {
1729    ///                 // response for an `position` streamed in
1730    ///                 println!("{position:?}");
1731    ///
1732    ///                 if position.long_short == PositionType::Long {
1733    ///                     if position.last < position.average_price {
1734    ///                         losing_positions.push(*position)
1735    ///                     }
1736    ///                 } else if position.long_short == PositionType::Short {
1737    ///                     if position.last > position.average_price {
1738    ///                         losing_positions.push(*position)
1739    ///                     }
1740    ///                 }
1741    ///
1742    ///                 // do something with the list of losing trades
1743    ///                 // maybe send email or text of the positions
1744    ///                 println!("{losing_positions:?}");
1745    ///             }
1746    ///             StreamPositionsResp::Heartbeat(heartbeat) => {
1747    ///                 // response for periodic signals letting you know the connection is
1748    ///                 // still alive. a heartbeat is sent every 5 seconds of inactivity.
1749    ///                 println!("{heartbeat:?}");
1750    ///
1751    ///                 // for the sake of this example after we recieve the
1752    ///                 // tenth heartbeat, we will stop the stream session.
1753    ///                 if heartbeat.heartbeat > 10 {
1754    ///                     // example: stopping a stream connection
1755    ///                     return Err(Error::StopStream);
1756    ///                 }
1757    ///             }
1758    ///             StreamPositionsResp::Status(status) => {
1759    ///                 // signal sent on state changes in the stream
1760    ///                 // (closed, opened, paused, resumed)
1761    ///                 println!("{status:?}");
1762    ///             }
1763    ///             StreamPositionsResp::Error(err) => {
1764    ///                 // response for when an error was encountered,
1765    ///                 // with details on the error
1766    ///                 println!("{err:?}");
1767    ///             }
1768    ///         }
1769    ///
1770    ///         Ok(())
1771    ///     })
1772    ///     .await?;
1773    /// ```
1774    fn stream_positions<'a, F>(
1775        &'a self,
1776        mut on_chunk: &'a mut F,
1777        client: &'a mut Client,
1778    ) -> Self::StreamPositionsFuture<'a>
1779    where
1780        F: FnMut(StreamPositionsResp) -> Result<(), Error> + Send + 'a,
1781    {
1782        let account_ids: Vec<&str> = self
1783            .iter()
1784            .map(|account| account.account_id.as_str())
1785            .collect();
1786
1787        Box::pin(async move {
1788            let positions =
1789                Account::stream_positions_for_accounts(client, account_ids, &mut on_chunk).await?;
1790            Ok(positions)
1791        })
1792    }
1793}
1794
1795#[derive(Clone, Debug, Deserialize, Serialize)]
1796#[serde(rename_all = "PascalCase")]
1797/// Deeper Details for an `Account`
1798pub struct AccountDetail {
1799    /// Can account locate securities for borrowing?
1800    ///
1801    /// For example if you want to short a stock, you need
1802    /// to "locate" shares to borrow and sell.
1803    is_stock_locate_eligible: bool,
1804    /// Is account enrolled with Regulation T ?
1805    ///
1806    /// Regulation T governs cash accounts and the amount of credit that
1807    /// broker-dealers can extend to investors for the purchase of securities.
1808    enrolled_in_reg_t_program: bool,
1809    /// Does the account require a buying power warning before order execution?
1810    ///
1811    /// TradeStation uses the greater of Overnight Buying Power or Day Trade
1812    /// Buying Power to determine if an order should be accepted or rejected.
1813    ///
1814    /// In cases where an order exceeds both values, the order will be rejected.
1815    /// If the order exceeds only one of the values, a Buying Power Warning will
1816    /// appear to notify you that the order could result in a margin call.
1817    requires_buying_power_warning: bool,
1818    /// Is the `Account` qualified for day trading?
1819    ///
1820    /// An `Account` MUST maintain a minimum equity balance of $25,000
1821    /// to be qualified for day trades. *(As per TradeStation compliance rules)*
1822    day_trading_qualified: bool,
1823    /// What options level is the `Account` approved for?
1824    ///
1825    /// The option approval level will determine what options strategies you will
1826    /// be able to employ on `Account`. In general terms, the levels are defined as:
1827    /// Level 0: No options trading allowed.
1828    /// Level 1: Writing of Covered Calls, Buying Protective Puts.
1829    /// Level 2: Level 1 + Buying Calls, Buying Puts, Writing Covered Puts.
1830    /// Level 3: Level 2 + Stock Option Spreads, Index Option Spreads, Butterfly Spreads, Condor Spreads, Iron Butterfly Spreads, Iron Condor Spreads.
1831    /// Level 4: Level 3 + Writing of Naked Puts (Stock Options).
1832    /// Level 5: Level 4 + Writing of Naked Puts (Index Options), Writing of Naked Calls (Stock Options), Writing of Naked Calls (Index Options).
1833    ///
1834    /// These levels vary depending on the funding and type of account.
1835    // TODO: Make enum for this
1836    option_approval_level: u8,
1837    /// Is the `Account` a Pattern Day Trader?
1838    ///
1839    /// As per FINRA rules, an `Account` will be considered a pattern day trader
1840    /// if it day-trades 4 or more times in 5 business days and it's day-trading
1841    /// activities are greater than 6 percent of it's total trading activity for
1842    /// that same five-day period.
1843    ///
1844    /// A pattern day trader must maintain minimum equity of $25,000 on any day
1845    /// that the customer day trades. If the account falls below the $25,000
1846    /// requirement, the pattern day trader will not be permitted to day trade
1847    /// until the account is restored to the $25,000 minimum equity level.
1848    pattern_day_trader: bool,
1849    /// Is the `Account` enabled to trade crypto?
1850    ///
1851    /// NOTE: As of 2024 TradeStation no longer offer's crypto trading.
1852    crypto_enabled: bool,
1853}
1854
1855#[derive(Clone, Debug, Deserialize, Serialize)]
1856#[serde(rename_all = "PascalCase")]
1857/// The real time balance of an `Account`.
1858pub struct Balance {
1859    #[serde(rename = "AccountID")]
1860    /// The main identifier for a TradeStation account
1861    pub account_id: String,
1862    /// The type of account, examples: "Cash" or "Margin"
1863    pub account_type: String,
1864    /// The real time Cash Balance value for the `Account`
1865    pub cash_balance: String,
1866    /// The real time Buying Power value for the `Account`
1867    pub buying_power: String,
1868    /// The real time Equity value for the `Account`
1869    pub equity: String,
1870    /// The real time Market Value for the `Account`
1871    pub market_value: String,
1872    #[serde(rename = "TodaysProfitLoss")]
1873    /// The real time (profit - loss) value for the `Account` over a 24 hour period
1874    pub todays_pnl: String,
1875    /// The value of uncleared funds for the `Account`
1876    pub uncleared_deposit: String,
1877    /// Deeper details on the `Balance` of an `Account`
1878    pub balance_detail: BalanceDetail,
1879    /// The amount paid in brokerage commissions.
1880    ///
1881    /// NOTE: This value does not include slippage.
1882    pub commission: String,
1883}
1884#[derive(Clone, Debug, Deserialize, Serialize)]
1885#[serde(rename_all = "PascalCase")]
1886/// Real time balance information for an `Account`.
1887pub struct BalanceDetail {
1888    /// The real time cost for all positions open in the `Account`
1889    ///
1890    /// NOTE: Positions are based on the actual entry price
1891    pub cost_of_positions: Option<String>,
1892    /// The number of day trades the `Account` has taken over the previous 4 days
1893    ///
1894    /// NOTE: This updates daily
1895    ///
1896    /// NOTE: This is always None for futures `Account`.
1897    pub day_trades: Option<String>,
1898    /// The real time dollar amount of required funds for `Account` margin maintenance
1899    ///
1900    /// NOTE: SUM(maintenance margin of all open positions in the account).
1901    ///
1902    /// NOTE: This is always None for futures `Account`.
1903    pub maintenance_rate: Option<String>,
1904    /// The real time value of intraday buying power for options
1905    ///
1906    /// NOTE: This is always None for futures `Account`.
1907    pub option_buying_power: Option<String>,
1908    /// The real time Market Value of current open option positions in an `Account`.
1909    pub options_market_value: Option<String>,
1910    /// The real time Buying Power value that can be held overnight w/o triggering a margin call.
1911    ///
1912    /// NOTE: (Equity - Overnight Requirement %) / 50 %.
1913    pub overnight_buying_power: Option<String>,
1914    /// The real time dollar value of open order Day Trade Margins for an `Account`.
1915    ///
1916    /// NOTE: SUM(Day Trade Margin of all open orders in the account).
1917    ///
1918    /// NOTE: Always `None` for cash & margin accounts
1919    pub day_trade_open_order_margin: Option<String>,
1920    /// The real time dollar value of open order Initial Margin for an `Account`.
1921    ///
1922    /// NOTE: SUM(Initial Margin of all open orders in the account).
1923    ///
1924    /// NOTE: Always `None` for cash & margin accounts.
1925    pub open_order_margin: Option<String>,
1926    /// The real time dollar value of Initial Margin for an `Account`.
1927    ///
1928    /// NOTE: SUM(Initial Margin of all open positions in the account).
1929    pub initial_margin: Option<String>,
1930    /// The real time dollar value of Maintenance Margin for an `Account`.
1931    ///
1932    /// NOTE: SUM(Maintenance Margins of all open positions in the account).
1933    ///
1934    /// NOTE: Always `None` for cash & margin accounts.
1935    pub maintenance_margin: Option<String>,
1936    /// The real time dollar amount of Trade Equity for an `Account`.
1937    ///
1938    /// NOTE: Always `None` for cash & margin accounts.
1939    pub trade_equity: Option<String>,
1940    /// The value of special securities deposited with the clearing firm
1941    /// for the sole purpose of increasing purchasing power in `Account`
1942    ///
1943    /// NOTE: This number will be reset daily by the account balances clearing file.
1944    ///
1945    /// NOTE: The entire value of this field will increase purchasing power.
1946    ///
1947    /// NOTE: Always `None` for cash & margin accounts.
1948    pub security_on_deposit: Option<String>,
1949    /// The real time dollar value of Today's Trade Equity for an `Account`.
1950    ///
1951    /// NOTE: (Beginning Day Trade Equity - Real Time Trade Equity).
1952    pub today_real_time_trade_equity: Option<String>,
1953    /// Deeper details on base currency.
1954    ///
1955    /// NOTE: Always `None` for cash & margin accounts.
1956    pub currency_details: Option<CurrencyDetails>,
1957    /// The real time amount of required funds for `Account` margin maintenance.
1958    ///
1959    /// NOTE: The currency denomination is dependant on `Account::currency`.
1960    ///
1961    /// NOTE: SUM(maintenance margin of all open positions in the account).
1962    ///
1963    /// NOTE: Always `None` for futures accounts.
1964    pub required_margin: Option<String>,
1965    /// Funds received by TradeStation that are not settled from a transaction in the `Account`.
1966    ///
1967    /// NOTE: Always `None` for futures accounts.
1968    pub unsettled_funds: Option<String>,
1969    /// Maintenance Excess.
1970    ///
1971    /// NOTE: (Cash Balance + Long Market Value + Short Credit - Maintenance Requirement - Margin Debt - Short Market Value).
1972    pub day_trade_excess: String,
1973    #[serde(rename = "RealizedProfitLoss")]
1974    /// The net Realized Profit or Loss of an `Account` for the current trading day.
1975    ///
1976    /// NOTE: This includes all commissions and routing fees.
1977    pub realized_pnl: String,
1978    #[serde(rename = "UnrealizedProfitLoss")]
1979    /// The net Unrealized Profit or Loss of an `Account` for all currently open positions.
1980    ///
1981    /// NOTE: This does not include commissions or routing fees.
1982    pub unrealized_pnl: String,
1983}
1984
1985#[derive(Clone, Debug, Deserialize, Serialize)]
1986#[serde(rename_all = "PascalCase")]
1987/// The beginning of day balance of an `Account`.
1988pub struct BODBalance {
1989    #[serde(rename = "AccountID")]
1990    /// The main identifier for a TradeStation account.
1991    pub account_id: String,
1992    /// The type of account, examples: "Cash" or "Margin".
1993    pub account_type: String,
1994    /// Deeper details on the `Balance` of an `Account`.
1995    pub balance_detail: BODBalanceDetail,
1996    /// Deeper details on the `Currency` local of an `Account`.
1997    ///
1998    /// NOTE: Only applies to futures.
1999    pub currency_details: Option<Vec<BODCurrencyDetails>>,
2000}
2001#[derive(Clone, Debug, Deserialize, Serialize)]
2002#[serde(rename_all = "PascalCase")]
2003/// The beginning of day balance information of an `Account`.
2004pub struct BODBalanceDetail {
2005    /// The amount of cash in the account at the beginning of the day.
2006    ///
2007    /// NOTE: Only applies to equities.
2008    pub account_balance: Option<String>,
2009    /// Beginning of day value for cash available to withdraw
2010    pub cash_available_to_withdraw: Option<String>,
2011    /// The number of day trades placed in the account within the previous
2012    /// 4 trading days.
2013    ///
2014    /// NOTE: Only applies to equities.
2015    pub day_trades: Option<String>,
2016    /// The Intraday Buying Power with which the account started the trading day.
2017    ///
2018    /// NOTE: Only applies to equities.
2019    pub day_trading_marginable_buying_power: Option<String>,
2020    /// The total amount of equity with which you started the current trading day.
2021    pub equity: String,
2022    /// The amount of cash in the account at the beginning of the day.
2023    pub net_cash: String,
2024    /// Unrealized profit and loss at the beginning of the day.
2025    ///
2026    /// NOTE: Only applies to futures.
2027    pub open_trade_equity: Option<String>,
2028    /// Option buying power at the start of the trading day.
2029    ///
2030    /// NOTE: Only applies to equities.
2031    pub option_buying_power: Option<String>,
2032    /// Intraday liquidation value of option positions.
2033    ///
2034    /// NOTE: Only applies to equities.
2035    pub option_value: Option<String>,
2036    /// Overnight Buying Power (Regulation T) at the start of the trading day.
2037    ///
2038    /// NOTE: Only applies to equities.
2039    pub overnight_buying_power: Option<String>,
2040    /// The value of special securities that are deposited by the customer with
2041    /// the clearing firm for the sole purpose of increasing purchasing power in
2042    /// their trading account.
2043    ///
2044    /// NOTE: Only applies to futures.
2045    pub security_on_deposit: Option<String>,
2046}
2047#[derive(Clone, Debug, Deserialize, Serialize)]
2048#[serde(rename_all = "PascalCase")]
2049/// The beginning of day currency information.
2050///
2051/// NOTE: Only applies to futures.
2052pub struct BODCurrencyDetails {
2053    /// The dollar amount of Beginning Day Margin for the given forex account
2054    pub account_margin_requirement: Option<String>,
2055    /// The dollar amount of Beginning Day Trade Equity for the given account
2056    pub account_open_trade_equity: String,
2057    /// The value of special securities that are deposited by the customer with
2058    /// the clearing firm for the sole purpose of increasing purchasing power in
2059    /// their trading account.
2060    ///
2061    /// NOTE: This number will be reset daily by the account balances
2062    /// clearing file.
2063    ///
2064    /// NOTE: The entire value of this field will increase purchasing power
2065    pub account_securities: String,
2066    /// The dollar amount of the Beginning Day Cash Balance for the given account
2067    pub cash_balance: String,
2068    /// The currency of the entity
2069    pub currency: String,
2070    /// The dollar amount of Beginning Day Margin for the given forex account
2071    pub margin_requirement: Option<String>,
2072    /// The dollar amount of Beginning Day Trade Equity for the given account
2073    pub open_trade_equity: String,
2074    /// Indicates the dollar amount of Beginning Day Securities
2075    pub securities: String,
2076}
2077
2078#[derive(Clone, Debug, Deserialize, Serialize)]
2079#[serde(rename_all = "PascalCase")]
2080/// The properties that describe balance characteristics in different currencies.
2081///
2082/// NOTE: Only applies to futures.
2083pub struct CurrencyDetails {
2084    /// Base currency.
2085    currency: String,
2086    /// The net Unrealized Profit or Loss for all currently open positions.
2087    ///
2088    /// NOTE: This does not include commissions or routing fees.
2089    commission: String,
2090    /// The real time value of an `Account`(s) Cash Balance.
2091    cash_balance: String,
2092    #[serde(rename = "RealizedProfitLoss")]
2093    /// The net Realized Profit or Loss of an `Account` for the current trading day.
2094    ///
2095    /// NOTE: This includes all commissions and routing fees.
2096    realized_pnl: String,
2097    #[serde(rename = "UnrealizedProfitLoss")]
2098    /// The net Unrealized Profit or Loss of an `Account` for all currently open positions.
2099    ///
2100    /// NOTE: This does not include commissions or routing fees.
2101    unrealized_pnl: String,
2102    /// The real time dollar value of Initial Margin for an `Account`.
2103    ///
2104    /// NOTE: SUM(Initial Margin of all open positions in the account).
2105    initial_margin: String,
2106    /// The real time dollar value of Maintenance Margin for an `Account`.
2107    ///
2108    /// NOTE: SUM(Maintenance Margins of all open positions in the account).
2109    maintenance_margin: String,
2110    /// The real time conversion rate used to translate value from symbol currency to `Account` currency.
2111    account_conversion_rate: String,
2112}
2113
2114#[derive(Clone, Debug, Deserialize, Serialize)]
2115#[serde(rename_all = "PascalCase")]
2116/// An order to open, close, add, or trim positions.
2117pub struct Order {
2118    #[serde(rename = "AccountID")]
2119    /// The `Account` id to the this `Order` belongs to.
2120    pub account_id: String,
2121    /// The `Order rules` or brackets.
2122    pub advanced_options: Option<String>,
2123    /// The Closed Date Time of this `Order`.
2124    pub closed_date_time: Option<String>,
2125    /// The actual brokerage commission cost and routing fees
2126    /// for a trade based on the number of shares or contracts.
2127    pub commission_fee: String,
2128    /// The relationship between linked `Order`(s) in a group
2129    /// and this `Order` in specific.
2130    pub conditional_orders: Option<ConditionalOrder>,
2131    /// The rate used to convert from the currency of
2132    /// the symbol to the currency of the account.
2133    pub conversion_rate: Option<String>,
2134    /// The currency used to complete the `Order`.
2135    pub currency: String,
2136    /// The amount of time for which the `Order` is valid.
2137    pub duration: String,
2138    /// At the top level, this is the average fill price.
2139    ///
2140    /// At the expanded levels, this is the actual execution price.
2141    pub filled_price: Option<String>,
2142    /// The expiration date-time for the `Order`
2143    ///
2144    /// NOTE: The time portion, if `"T:00:00:00Z"`, should be ignored.
2145    pub good_till_date: Option<String>,
2146    /// An identifier for `Order`(s) that are part of the same bracket.
2147    pub group_name: Option<String>,
2148    /// Legs (multi step/part trade) associated with this `Order`
2149    pub legs: Vec<OrderLeg>,
2150    /// Allows you to specify when an order will be placed based on
2151    /// the price action of one or more symbols.
2152    // TODO: Should I convert None to empty vector ?
2153    pub market_activation_rules: Option<Vec<MarketActivationRule>>,
2154    /// Allows you to specify a time that an `Order` will be placed.
2155    // TODO: Should I convert None to empty vector ?
2156    pub time_activation_rules: Option<Vec<TimeActivationRule>>,
2157    /// The limit price for Limit and Stop Limit `Order`(s).
2158    pub limit_price: Option<String>,
2159    /// Time the `Order` was placed.
2160    pub opened_date_time: String,
2161    #[serde(rename = "OrderID")]
2162    /// The `Order` id.
2163    pub order_id: String,
2164    /// The type of `Order` this is.
2165    pub order_type: OrderType,
2166    /// Price used for the buying power calculation of the `Order`.
2167    pub price_used_for_buying_power: String,
2168    /// Identifies the routing selection made by the customer when
2169    /// placing the `Order`.
2170    ///
2171    /// NOTE: ONLY valid for Equities.
2172    pub routing: Option<String>,
2173    /// Hides the true number of shares intended to be bought or sold.
2174    ///
2175    /// NOTE: ONLY valid for `OrderType::Limit` or `Order::Type::StopLimit`
2176    /// `Order`(s).
2177    ///
2178    /// NOTE: Not valid for all exchanges.
2179    pub show_only_quantity: Option<String>,
2180    /// The spread type for an option `Order`
2181    pub spread: Option<String>,
2182    /// The status of an `Order`
2183    // TODO: make enum for this.
2184    pub status: String,
2185    /// Description of the `Order` status
2186    pub status_description: String,
2187    /// The stop price for `OrderType::StopLimit` and
2188    /// `OrderType::StopMarket` orders.
2189    pub stop_price: Option<String>,
2190    /// TrailingStop offset.
2191    ///
2192    /// NOTE: amount or percent.
2193    pub trailing_stop: Option<TrailingStop>,
2194    /// Only applies to equities.
2195    ///
2196    /// NOTE: Will contain a value if the order has received a routing fee.
2197    pub unbundled_route_fee: Option<String>,
2198}
2199
2200#[derive(Clone, Debug, Deserialize, Serialize)]
2201#[serde(rename_all = "PascalCase")]
2202pub struct TrailingStop {
2203    /// Currency Offset from current price.
2204    ///
2205    /// NOTE: Mutually exclusive with Percent.
2206    pub amount: Option<String>,
2207    /// Percentage offset from current price.
2208    ///
2209    /// NOTE: Mutually exclusive with Amount.
2210    pub percent: Option<String>,
2211}
2212
2213#[derive(Clone, Debug, Deserialize, Serialize)]
2214/// Types of `Order`(s).
2215pub enum OrderType {
2216    /// Limit Order
2217    Limit,
2218    /// Market Order
2219    Market,
2220    /// Stop Loss At Market Order
2221    StopMarket,
2222    /// Stop Loss At Limit Order
2223    StopLimit,
2224}
2225
2226#[derive(Clone, Debug, Deserialize, Serialize)]
2227#[serde(rename_all = "PascalCase")]
2228/// Allows you to specify a time that an `Order` will be placed.
2229pub struct TimeActivationRule {
2230    /// Timestamp represented as an RFC3339 formatted date.
2231    time_utc: String,
2232}
2233
2234#[derive(Clone, Debug, Deserialize, Serialize)]
2235#[serde(rename_all = "PascalCase")]
2236/// Allows you to specify when an order will be placed
2237/// based on the price action of one or more symbols.
2238pub struct MarketActivationRule {
2239    /// The type of activation rule.
2240    ///
2241    /// NOTE: Currently only supports `"Price"` for now.
2242    pub rule_type: String,
2243    /// The symbol that the rule is based on.
2244    pub symbol: String,
2245    /// The type of comparison predicate the rule is based on.
2246    pub predicate: Predicate,
2247    /// The ticks behavior for the activation rule.
2248    pub tigger_key: TickTrigger,
2249    /// The price at which the rule will trigger.
2250    pub price: Option<String>,
2251    /// Relation with the previous activation rule.
2252    ///
2253    /// NOTE: The first rule will never have a logic operator.
2254    pub logic_operator: Option<LogicOp>,
2255}
2256
2257#[derive(Clone, Debug, Deserialize, Serialize)]
2258/// Logic Operators
2259pub enum LogicOp {
2260    And,
2261    Or,
2262}
2263
2264#[derive(Clone, Debug, Deserialize, Serialize)]
2265/// Types of tick triggers
2266pub enum TickTrigger {
2267    /// Single Trade Tick
2268    STT,
2269    /// Single Trade Tick Within NBBO
2270    STTN,
2271    /// Single Bid/Ask Tick
2272    SBA,
2273    /// Single Ask/Bid Tick
2274    SAB,
2275    /// Double Trade Tick
2276    DTT,
2277    /// Double Trade Tick Within NBBO
2278    DTTN,
2279    /// Double Bid/Ask Tick
2280    DBA,
2281    /// Double Ask/Bid Tick
2282    DAB,
2283    /// Triple Trade Tick
2284    TTT,
2285    /// Triple Trade Tick Within NBBO
2286    TTTN,
2287    /// Triple Bid/Ask Tick
2288    TBA,
2289    /// Triple Ask/Bid Tick
2290    TAB,
2291}
2292
2293#[derive(Clone, Debug, Deserialize, Serialize)]
2294/// Types of comparison predicates
2295pub enum Predicate {
2296    /// Less than
2297    Lt,
2298    /// Less than or Equal
2299    Lte,
2300    /// Greater than
2301    Gt,
2302    /// Greater than or Equal
2303    Gte,
2304}
2305
2306#[derive(Clone, Debug, Deserialize, Serialize)]
2307#[serde(rename_all = "PascalCase")]
2308/// Order Leg associated with this `Order`
2309pub struct OrderLeg {
2310    /// Indicates the asset type of the `Order`.
2311    pub asset_type: AssetType,
2312    /// identifier for the `Order` action (buying or selling)
2313    pub buy_or_sell: OrderAction,
2314    /// Number of shares or contracts that have been executed.
2315    pub exec_quantity: String,
2316    /// The price at which `Order` execution occurred.
2317    pub execution_price: Option<String>,
2318    /// The expiration date of the future or option contract.
2319    pub expiration_date: Option<String>,
2320    /// The stage of the `Order` , is it opening or closing?
2321    pub open_or_close: Option<OrderStage>,
2322    /// The type of option
2323    pub option_type: Option<OptionType>,
2324    /// Number of shares or contracts being purchased or sold.
2325    pub quantity_ordered: String,
2326    /// In a partially filled `Order` , this is the number of shares
2327    /// or contracts the have NOT yet been filled.
2328    pub quantity_remaining: String,
2329    /// The price at which the holder of an options contract can buy
2330    /// or sell the underlying asset.
2331    ///
2332    /// NOTE: ONLY for option `Order`(s).
2333    pub strike_price: Option<String>,
2334    /// The securities symbol the `Order` is for.
2335    pub symbol: String,
2336    /// The underlying securities symbol the `Order` is for.
2337    ///
2338    /// NOTE: ONLY for futures and options.
2339    pub underlying: Option<String>,
2340}
2341
2342#[derive(Clone, Debug, Deserialize, Serialize)]
2343/// The type of option
2344pub enum OptionType {
2345    #[serde(rename = "CALL")]
2346    /// Call Option
2347    Call,
2348    #[serde(rename = "PUT")]
2349    /// Put Option
2350    Put,
2351}
2352
2353#[derive(Clone, Debug, Deserialize, Serialize)]
2354/// The stage of the `Order` , is it opening or closing?
2355pub enum OrderStage {
2356    /// Order to open position.
2357    Open,
2358    /// Order to close position.
2359    Close,
2360}
2361
2362#[derive(Clone, Debug, Deserialize, Serialize)]
2363/// The different types of order actions.
2364pub enum OrderAction {
2365    /// Buying to open.
2366    Buy,
2367    /// Selling to close.
2368    Sell,
2369    /// Open a short position.
2370    SellShort,
2371    /// Closing a short position.
2372    BuyToCover,
2373}
2374
2375#[derive(Clone, Debug, Deserialize, Serialize)]
2376/// The different types of asset's.
2377pub enum AssetType {
2378    #[serde(rename = "UNKNOWN")]
2379    Unknown,
2380    #[serde(rename = "STOCK")]
2381    Stock,
2382    #[serde(rename = "STOCKOPTION")]
2383    StockOption,
2384    #[serde(rename = "FUTURE")]
2385    Future,
2386    #[serde(rename = "FUTUREOPTION")]
2387    FutureOption,
2388    #[serde(rename = "FOREX")]
2389    Forex,
2390    #[serde(rename = "CURRENCYOPTION")]
2391    CurrencyOption,
2392    #[serde(rename = "INDEX")]
2393    Index,
2394    #[serde(rename = "INDEXOPTION")]
2395    IndexOption,
2396}
2397
2398#[derive(Clone, Debug, Deserialize, Serialize)]
2399#[serde(rename_all = "PascalCase")]
2400/// Describes the relationship between linked
2401/// orders in a group and this order.
2402pub struct ConditionalOrder {
2403    #[serde(rename = "OrderID")]
2404    /// The id of the linked `Order`.
2405    pub order_id: String,
2406    /// The relationship of a linked order within a group order
2407    /// to the current returned `Order`.
2408    pub relationship: OrderRelationship,
2409}
2410
2411#[derive(Clone, Debug, Deserialize, Serialize)]
2412/// Types of `Order` relationships
2413pub enum OrderRelationship {
2414    BRK,
2415    OSP,
2416    OSO,
2417    OCO,
2418}
2419
2420#[derive(Clone, Debug, Deserialize, Serialize)]
2421#[serde(rename_all = "PascalCase")]
2422/// The open trades (positons).
2423pub struct Position {
2424    #[serde(rename = "AccountID")]
2425    /// The `Account` id the `Position` belongs to.
2426    pub account_id: String,
2427    /// Indicates the asset type of the position.
2428    // NOTE: use enum
2429    pub asset_type: String,
2430    /// The average price of the position currently held.
2431    pub average_price: String,
2432    /// The highest price a prospective buyer is prepared to pay at
2433    /// a particular time for a trading unit of a given symbol.
2434    pub bid: String,
2435    /// The price at which a security, futures contract, or other
2436    /// financial instrument is offered for sale.
2437    pub ask: String,
2438    /// The currency conversion rate that is used in order to convert
2439    /// from the currency of the symbol to the currency of the account.
2440    pub conversion_rate: String,
2441    /// DayTradeMargin used on open positions.
2442    ///
2443    /// NOTE: Currently only calculated for futures positions.
2444    /// Other asset classes will have a 0 for this value.
2445    pub day_trade_requirement: String,
2446    /// The UTC formatted expiration date of the future or option symbol,
2447    /// in the country the contract is traded in.
2448    ///
2449    /// NOTE: The time portion of the value should be ignored.
2450    pub expiration_date: Option<String>,
2451    /// The margin account balance denominated in the symbol currency required
2452    /// for entering a position on margin.
2453    ///
2454    /// NOTE: Only applies to future and option positions.
2455    pub initial_requirement: String,
2456    /// The last price at which the symbol traded.
2457    pub last: String,
2458    /// Specifies if the position is Long or Short.
2459    pub long_short: PositionType,
2460    /// The MarkToMarketPrice value is the weighted average of the previous close
2461    /// price for the position quantity held overnight and the purchase price of the
2462    /// position quantity opened during the current market session.
2463    ///
2464    /// NOTE: This value is used to calculate TodaysProfitLoss.
2465    ///
2466    /// NOTE: Only applies to equity and option positions.
2467    pub mark_to_market_price: String,
2468    /// The actual market value denominated in the symbol currency of the open position.
2469    ///
2470    /// NOTE: This value is updated in real-time.
2471    pub market_value: String,
2472    #[serde(rename = "PositionID")]
2473    /// A unique identifier for the position.
2474    pub position_id: String,
2475    /// The number of shares or contracts for a particular position.
2476    ///
2477    /// NOTE: This value is negative for short positions.
2478    pub quantity: String,
2479    /// Symbol of the position.
2480    pub symbol: String,
2481    /// Time the position was entered.
2482    pub timestamp: String,
2483    /// The unrealized profit or loss denominated in the account currency on the position
2484    /// held, calculated using the MarkToMarketPrice.
2485    ///
2486    /// NOTE: Only applies to equity and option positions.
2487    #[serde(rename = "TodaysProfitLoss")]
2488    pub todays_pnl: String,
2489    /// The total cost denominated in the account currency of the open position.
2490    pub total_cost: String,
2491    #[serde(rename = "UnrealizedProfitLoss")]
2492    /// The unrealized profit or loss denominated in the symbol currency on the position
2493    /// held, calculated based on the average price of the position.
2494    pub unrealized_pnl: String,
2495    #[serde(rename = "UnrealizedProfitLossPercent")]
2496    /// The unrealized profit or loss on the position expressed as a percentage of the
2497    /// initial value of the position.
2498    pub unrealized_pnl_percent: String,
2499    #[serde(rename = "UnrealizedProfitLossQty")]
2500    /// The unrealized profit or loss denominated in the account currency divided by the
2501    /// number of shares, contracts or units held.
2502    pub unrealized_pnl_qty: String,
2503}
2504
2505#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
2506/// A position type can either be short or long
2507pub enum PositionType {
2508    /// Long a share, or futures/options contract
2509    Long,
2510    /// Short a share, or futures/options contract
2511    Short,
2512}
2513
2514impl Client {
2515    /// Get all of your registered TradeStation `Accounts`
2516    pub async fn get_accounts(&mut self) -> Result<Vec<Account>, Error> {
2517        Account::get_all(self).await
2518    }
2519
2520    /// Get a specific TradeStation `Account` by it's account id
2521    pub async fn get_account(&mut self, account_id: &str) -> Result<Account, Error> {
2522        Account::get(self, account_id).await
2523    }
2524
2525    /// Get the current balance of a specific `Account` by it's account id
2526    pub async fn get_account_balance(&mut self, account_id: &str) -> Result<Balance, Error> {
2527        let mut balances = Account::get_balances_by_ids(self, vec![account_id]).await?;
2528        if balances.len() == 1 {
2529            // NOTE: This unwrap is panic safe due to invariant above
2530            let balance = balances.pop().unwrap();
2531            Ok(balance)
2532        } else {
2533            Err(Error::AccountNotFound)
2534        }
2535    }
2536
2537    /// Get the current balance of all `Account`(s)
2538    pub async fn get_account_balances(
2539        &mut self,
2540        account_ids: Vec<&str>,
2541    ) -> Result<Vec<Balance>, Error> {
2542        Account::get_balances_by_ids(self, account_ids).await
2543    }
2544}