Skip to main content

ibkr_flex_statement/
equity_summary.rs

1use super::currency::Currency;
2use super::time_utils;
3use crate::node_utils::NodeWrapper;
4use crate::statement_section::StatementSection;
5use anyhow::Result;
6
7#[derive(Debug, PartialEq)]
8pub struct EquitySummary {
9    pub account_id: String,
10    pub cash_balance: f64,
11    pub cash_balance_long: f64,
12    pub cash_balance_short: f64,
13    pub currency: Currency,
14    pub interest_accrual_mtd: f64,
15    pub interest_accrual_mtd_long: f64,
16    pub interest_accrual_mtd_short: f64,
17    pub stock_balance: f64,
18    pub stock_balance_long: f64,
19    pub stock_balance_short: f64,
20    pub timestamp_eod_ms: i64,
21}
22
23impl StatementSection for EquitySummary {
24    fn from_node(node: &NodeWrapper) -> Result<EquitySummary> {
25        Ok(EquitySummary {
26            account_id: node.get_attribute("accountId")?,
27            cash_balance: node.parse_attribute("cash")?,
28            cash_balance_long: node.parse_attribute("cashLong")?,
29            cash_balance_short: node.parse_attribute("cashShort")?,
30            currency: Currency::try_from(node.node.attribute("currency").unwrap())?,
31            interest_accrual_mtd: node.parse_attribute("interestAccruals")?,
32            interest_accrual_mtd_long: node.parse_attribute("interestAccrualsLong")?,
33            interest_accrual_mtd_short: node.parse_attribute("interestAccrualsShort")?,
34            stock_balance: node.parse_attribute("stock")?,
35            stock_balance_long: node.parse_attribute("stockLong")?,
36            stock_balance_short: node.parse_attribute("stockShort")?,
37            timestamp_eod_ms: time_utils::trading_eod_after_hours_timestamp_ms(
38                node.node.attribute("reportDate").unwrap(),
39            )?,
40        })
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use crate::Parser;
48    use crate::currency::Currency;
49    use anyhow::Result;
50
51    const PARTIAL_STATEMENT_EXAMPLE: &str = r##"
52        <FlexQueryResponse queryName="example-query" type="AF">
53            <FlexStatements count="1">
54                <FlexStatement accountId="U1234567" fromDate="2025-04-25" toDate="2025-04-25" period="LastBusinessDay" whenGenerated="2025-04-26;13:34:28 EDT">
55                    <AccountInformation accountId="U1234567" accountType="Individual" customerType="Individual" accountCapabilities="Portfolio Margin" tradingPermissions="Stocks,Options,Warrants,Forex,Futures,Crypto Currencies,Mutual Funds,Fully Paid Stock Loan" />
56                    <EquitySummaryInBase>
57                        <EquitySummaryByReportDateInBase accountId="U1234567" currency="USD" cash="-1755658.753685009" cashLong="0.000832235" cashShort="-1755658.754517244" commodities="0" commoditiesLong="0" commoditiesShort="0" dividendAccruals="0" dividendAccrualsLong="0" dividendAccrualsShort="0" interestAccruals="1292.18" interestAccrualsLong="1591.34" interestAccrualsShort="-299.16" stock="3441241" stockLong="3441241" stockShort="0" funds="0" fundsLong="0" fundsShort="0" brokerInterestAccrualsComponent="186.32" brokerInterestAccrualsComponentLong="485.48" brokerInterestAccrualsComponentShort="-299.16" brokerFeesAccrualsComponent="0" brokerFeesAccrualsComponentLong="0" brokerFeesAccrualsComponentShort="0" total="1686874.426314991" totalLong="3442832.340832235" totalShort="-1755957.914517244" reportDate="2025-04-24" />
58                        <EquitySummaryByReportDateInBase
59                            accountId="U1234567"
60                            currency="USD"
61                            cash="-1856140.99825062"
62                            cashLong="0.000832132"
63                            cashShort="-1856140.999082752"
64                            commodities="0"
65                            commoditiesLong="0"
66                            commoditiesShort="0"
67                            dividendAccruals="0"
68                            dividendAccrualsLong="0"
69                            dividendAccrualsShort="0"
70                            interestAccruals="1051.42"
71                            interestAccrualsLong="1591.34"
72                            interestAccrualsShort="-539.92"
73                            stock="3664457"
74                            stockLong="3664457"
75                            stockShort="0"
76                            funds="0"
77                            fundsLong="0"
78                            fundsShort="0"
79                            brokerInterestAccrualsComponent="-54.44"
80                            brokerInterestAccrualsComponentLong="485.48"
81                            brokerInterestAccrualsComponentShort="-539.92"
82                            brokerFeesAccrualsComponent="0"
83                            brokerFeesAccrualsComponentLong="0"
84                            brokerFeesAccrualsComponentShort="0"
85                            total="1809367.421749379"
86                            totalLong="3666048.340832131"
87                            totalShort="-1856680.919082752"
88                            reportDate="2025-04-25" />
89                    </EquitySummaryInBase>
90                </FlexStatement>
91            </FlexStatements>
92         </FlexQueryResponse>
93        "##;
94
95    #[test]
96    fn equity_summaries_parse() -> Result<()> {
97        let statements = Parser::new()?.parse_flex_query_response(PARTIAL_STATEMENT_EXAMPLE)?;
98        assert_eq!(statements.len(), 1);
99        let result = &statements[0];
100
101        // Ensure we got two equity summaries.
102        assert_eq!(result.equity_summaries.len(), 2);
103
104        assert_eq!(
105            result.equity_summaries[1],
106            EquitySummary {
107                account_id: "U1234567".to_string(),
108                cash_balance: -1856140.99825062,
109                cash_balance_long: 0.000832132,
110                cash_balance_short: -1856140.999082752,
111                currency: Currency::USD,
112                interest_accrual_mtd: 1051.42,
113                interest_accrual_mtd_long: 1591.34,
114                interest_accrual_mtd_short: -539.92,
115                stock_balance: 3664457.0,
116                stock_balance_long: 3664457.0,
117                stock_balance_short: 0.0,
118                timestamp_eod_ms: result.equity_summaries[1].timestamp_eod_ms,
119            }
120        );
121        Ok(())
122    }
123}