ibkr_flex_statement/
cash_report.rs

1use crate::currency::Currency;
2use crate::node_utils::NodeWrapper;
3use crate::statement_section::StatementSection;
4use crate::time_utils;
5use anyhow::Result;
6
7#[derive(Debug, PartialEq)]
8pub struct CashReport {
9    pub account_id: String,
10    pub currency: Currency,
11    pub start_timestamp_ms: i64,
12    pub end_timestamp_ms: i64,
13
14    pub starting_cash: f64,
15    pub ending_cash: f64,
16    pub ending_settled_cash: f64,
17
18    pub net_trades: Option<f64>,
19    pub net_trade_purchases: Option<f64>,
20    pub net_trade_sales: Option<f64>,
21
22    pub commissions: f64,
23    pub commissions_mtd: f64,
24    pub commissions_ytd: f64,
25
26    pub other_fees: f64,
27    pub other_fees_mtd: f64,
28    pub other_fees_ytd: f64,
29
30    pub dividends: f64,
31    pub dividends_mtd: f64,
32    pub dividends_ytd: f64,
33
34    pub interest: f64,
35    pub interest_mtd: f64,
36    pub interest_ytd: f64,
37
38    pub deposits: f64,
39    pub deposits_mtd: f64,
40    pub deposits_ytd: f64,
41
42    pub withdrawals: f64,
43    pub withdrawals_mtd: f64,
44    pub withdrawals_ytd: f64,
45}
46
47impl StatementSection for CashReport {
48    fn from_node(node: &NodeWrapper) -> Result<CashReport> {
49        let start_date_eod_ms_plus_one = time_utils::trading_eod_after_hours_timestamp_ms(
50            node.node.attribute("fromDate").unwrap(),
51        )?;
52        let start_timestamp_ms = start_date_eod_ms_plus_one - (60 * 60 * 24 * 1000) + 1;
53
54        Ok(CashReport {
55            account_id: node.get_attribute("accountId")?,
56            currency: Currency::try_from(node.node.attribute("currency").unwrap())?,
57
58            starting_cash: node.parse_attribute("startingCash")?,
59            ending_cash: node.parse_attribute("endingCash")?,
60            ending_settled_cash: node.parse_attribute("endingSettledCash")?,
61
62            net_trades: node.parse_attribute_opt("netTrades")?,
63            net_trade_purchases: node.parse_attribute_opt("netTradesPurchases")?,
64            net_trade_sales: node.parse_attribute_opt("netTradesSales")?,
65
66            commissions: node.parse_attribute("commissions")?,
67            commissions_mtd: node.parse_attribute("commissionsMTD")?,
68            commissions_ytd: node.parse_attribute("commissionsYTD")?,
69
70            other_fees: node.parse_attribute("otherFees")?,
71            other_fees_mtd: node.parse_attribute("otherFeesMTD")?,
72            other_fees_ytd: node.parse_attribute("otherFeesYTD")?,
73
74            dividends: node.parse_attribute("dividends")?,
75            dividends_mtd: node.parse_attribute("dividendsMTD")?,
76            dividends_ytd: node.parse_attribute("dividendsYTD")?,
77
78            interest: node.parse_attribute("brokerInterest")?,
79            interest_mtd: node.parse_attribute("brokerInterestMTD")?,
80            interest_ytd: node.parse_attribute("brokerInterestYTD")?,
81
82            deposits: node.parse_attribute("deposits")?,
83            deposits_mtd: node.parse_attribute("depositsMTD")?,
84            deposits_ytd: node.parse_attribute("depositsYTD")?,
85
86            withdrawals: node.parse_attribute("withdrawals")?,
87            withdrawals_mtd: node.parse_attribute("withdrawalsMTD")?,
88            withdrawals_ytd: node.parse_attribute("withdrawalsYTD")?,
89
90            start_timestamp_ms,
91            end_timestamp_ms: time_utils::trading_eod_after_hours_timestamp_ms(
92                node.node.attribute("toDate").unwrap(),
93            )?,
94        })
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::Parser;
102    use crate::currency::Currency;
103    use anyhow::Result;
104
105    const FULL_STATEMENT_EXAMPLE: &str = r##"
106        <FlexQueryResponse queryName="example-query" type="AF">
107            <FlexStatements count="1">
108                <FlexStatement accountId="U1234567" fromDate="2025-04-25" toDate="2025-04-25" period="LastBusinessDay" whenGenerated="2025-04-26;13:34:28 EDT">
109                    <AccountInformation accountId="U1234567" accountType="Individual" customerType="Individual" accountCapabilities="Portfolio Margin" tradingPermissions="Stocks,Options,Warrants,Forex,Futures,Crypto Currencies,Mutual Funds,Fully Paid Stock Loan" />
110                    <CashReport>
111                        <CashReportCurrency accountId="U1234567" currency="BASE_SUMMARY" fromDate="2025-04-25" toDate="2025-04-25" startingCash="-1755658.753685008" startingCashSec="-1755658.753685008" startingCashCom="0" commissions="-56.26956551" commissionsSec="-56.26956551" commissionsCom="0" commissionsMTD="-11167.4772929" commissionsYTD="-25339.56064716" depositWithdrawals="0" depositWithdrawalsSec="0" depositWithdrawalsCom="0" depositWithdrawalsMTD="0" depositWithdrawalsYTD="1650000" debitCardActivity="0" debitCardActivitySec="0" debitCardActivityCom="0" debitCardActivityMTD="0" debitCardActivityYTD="0" dividends="0" dividendsSec="0" dividendsCom="0" dividendsMTD="0" dividendsYTD="110.7" otherFees="-19.77" otherFeesSec="-19.77" otherFeesCom="0" otherFeesMTD="-121.27" otherFeesYTD="-486.9" otherIncome="0" otherIncomeSec="0" otherIncomeCom="0" otherIncomeMTD="0" otherIncomeYTD="0" endingCash="-1856140.99825062" endingCashSec="-1856140.99825062" endingCashCom="0" endingSettledCash="-1755734.79325062" endingSettledCashSec="-1755734.79325062" endingSettledCashCom="0" brokerInterest="0" brokerInterestSec="0" brokerInterestCom="0" brokerInterestMTD="-545.49" brokerInterestYTD="-1341.59" brokerFees="0" brokerFeesSec="0" brokerFeesCom="0" brokerFeesMTD="0" brokerFeesYTD="0" deposits="0" depositsSec="0" depositsCom="0" depositsMTD="0" depositsYTD="1650000" withdrawals="0" withdrawalsSec="0" withdrawalsCom="0" withdrawalsMTD="0" withdrawalsYTD="0" />
112                        <CashReportCurrency accountId="U1234567" currency="CAD" fromDate="2025-04-25" toDate="2025-04-25" startingCash="0.001153" startingCashSec="0.001153" startingCashCom="0" commissions="0" commissionsSec="0" commissionsCom="0" commissionsMTD="0" commissionsYTD="0" depositWithdrawals="0" depositWithdrawalsSec="0" depositWithdrawalsCom="0" depositWithdrawalsMTD="0" depositWithdrawalsYTD="0" debitCardActivity="0" debitCardActivitySec="0" debitCardActivityCom="0" debitCardActivityMTD="0" debitCardActivityYTD="0" dividends="0" dividendsSec="0" dividendsCom="0" dividendsMTD="0" dividendsYTD="0" otherFees="0" otherFeesSec="0" otherFeesCom="0" otherFeesMTD="0" otherFeesYTD="0" otherIncome="0" otherIncomeSec="0" otherIncomeCom="0" otherIncomeMTD="0" otherIncomeYTD="0" endingCash="0.001153" endingCashSec="0.001153" endingCashCom="0" endingSettledCash="0.001153" endingSettledCashSec="0.001153" endingSettledCashCom="0" brokerInterest="0" brokerInterestSec="0" brokerInterestCom="0" brokerInterestMTD="0" brokerInterestYTD="0" brokerFees="0" brokerFeesSec="0" brokerFeesCom="0" brokerFeesMTD="0" brokerFeesYTD="0" deposits="0" depositsSec="0" depositsCom="0" depositsMTD="0" depositsYTD="0" withdrawals="0" withdrawalsSec="0" withdrawalsCom="0" withdrawalsMTD="0" withdrawalsYTD="0" />
113                        <CashReportCurrency
114                            accountId="U1234567"
115                            currency="USD"
116                            fromDate="2025-04-25"
117                            toDate="2025-04-25"
118                            startingCash="-1755658.754517244"
119                            startingCashSec="-1755658.754517244"
120                            startingCashCom="0"
121                            commissions="-56.26956551"
122                            commissionsSec="-56.26956551"
123                            commissionsCom="0"
124                            commissionsMTD="-11167.4772929"
125                            commissionsYTD="-25339.56064716"
126                            depositWithdrawals="0"
127                            depositWithdrawalsSec="0"
128                            depositWithdrawalsCom="0"
129                            depositWithdrawalsMTD="0"
130                            depositWithdrawalsYTD="1650000"
131                            debitCardActivity="0"
132                            debitCardActivitySec="0"
133                            debitCardActivityCom="0"
134                            debitCardActivityMTD="0"
135                            debitCardActivityYTD="0"
136                            dividends="0"
137                            dividendsSec="0"
138                            dividendsCom="0"
139                            dividendsMTD="0"
140                            dividendsYTD="110.7"
141                            otherFees="-19.77"
142                            otherFeesSec="-19.77"
143                            otherFeesCom="0"
144                            otherFeesMTD="-121.27"
145                            otherFeesYTD="-486.9"
146                            otherIncome="0"
147                            otherIncomeSec="0"
148                            otherIncomeCom="0"
149                            otherIncomeMTD="0"
150                            otherIncomeYTD="0"
151                            endingCash="-1856140.999082752"
152                            endingCashSec="-1856140.999082752"
153                            endingCashCom="0"
154                            endingSettledCash="-1755734.794082752"
155                            endingSettledCashSec="-1755734.794082752"
156                            endingSettledCashCom="0"
157                            brokerInterest="0"
158                            brokerInterestSec="0"
159                            brokerInterestCom="0"
160                            brokerInterestMTD="-545.49"
161                            brokerInterestYTD="-1341.59"
162                            brokerFees="0"
163                            brokerFeesSec="0"
164                            brokerFeesCom="0"
165                            brokerFeesMTD="0"
166                            brokerFeesYTD="0"
167                            deposits="0"
168                            depositsSec="0"
169                            depositsCom="0"
170                            depositsMTD="0"
171                            depositsYTD="1650000"
172                            withdrawals="0"
173                            withdrawalsSec="0"
174                            withdrawalsCom="0"
175                            withdrawalsMTD="0"
176                            withdrawalsYTD="0" />
177                    </CashReport>
178
179                </FlexStatement>
180            </FlexStatements>
181         </FlexQueryResponse>
182        "##;
183
184    #[test]
185    fn cash_reports_parse() -> Result<()> {
186        let result = Parser::new()?.parse_statement_contents(FULL_STATEMENT_EXAMPLE)?;
187
188        // Ensure we got two equity summaries.
189        assert_eq!(result.cash_reports.len(), 3);
190
191        assert_eq!(
192            result.cash_reports[2],
193            CashReport {
194                account_id: "U1234567".to_string(),
195                currency: Currency::USD,
196
197                starting_cash: -1755658.754517244,
198                ending_cash: -1856140.999082752,
199                ending_settled_cash: -1755734.794082752,
200
201                commissions: -56.26956551,
202                commissions_mtd: -11167.4772929,
203                commissions_ytd: -25339.56064716,
204
205                dividends: 0.0,
206                dividends_mtd: 0.0,
207                dividends_ytd: 110.7,
208
209                other_fees: -19.77,
210                other_fees_mtd: -121.27,
211                other_fees_ytd: -486.9,
212
213                net_trades: None,
214                net_trade_purchases: None,
215                net_trade_sales: None,
216
217                interest: 0.0,
218                interest_mtd: -545.49,
219                interest_ytd: -1341.59,
220
221                deposits: 0.0,
222                deposits_mtd: 0.0,
223                deposits_ytd: 1650000.0,
224
225                withdrawals: 0.0,
226                withdrawals_mtd: 0.0,
227                withdrawals_ytd: 0.0,
228
229                start_timestamp_ms: result.cash_reports[2].start_timestamp_ms,
230                end_timestamp_ms: result.cash_reports[2].end_timestamp_ms,
231            }
232        );
233
234        Ok(())
235    }
236}