ibkr_flex_statement/
net_stock_position.rs

1use crate::asset_category::AssetCategory;
2use crate::currency::Currency;
3use crate::node_utils::NodeWrapper;
4use crate::statement_section::StatementSection;
5use anyhow::Result;
6
7#[derive(Debug, PartialEq)]
8pub struct NetStockPosition {
9    pub account_id: String,
10    pub asset_category: AssetCategory,
11    pub conid: u32,
12    pub currency: Currency,
13    pub listing_exchange: String,
14    pub net_shares: f64,
15    pub ticker: String,
16}
17
18impl StatementSection for NetStockPosition {
19    fn from_node(node: &NodeWrapper) -> Result<NetStockPosition> {
20        Ok(NetStockPosition {
21            account_id: node.get_attribute("accountId")?,
22            asset_category: AssetCategory::try_from(node.node.attribute("assetCategory").unwrap())?,
23            conid: node.parse_attribute("conid")?,
24            currency: Currency::try_from(node.node.attribute("currency").unwrap())?,
25            net_shares: node.parse_attribute("netShares")?,
26            listing_exchange: node.get_attribute("listingExchange")?,
27            ticker: node.get_attribute("symbol")?,
28        })
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35    use crate::Parser;
36    use crate::currency::Currency;
37    use anyhow::Result;
38
39    const PARTIAL_STATEMENT_EXAMPLE: &str = r##"
40        <FlexQueryResponse queryName="example-query" type="AF">
41            <FlexStatements count="1">
42                <FlexStatement accountId="U1234567" fromDate="2025-04-25" toDate="2025-04-25" period="LastBusinessDay" whenGenerated="2025-04-26;13:34:28 EDT">
43                    <AccountInformation accountId="U1234567" accountType="Individual" customerType="Individual" accountCapabilities="Portfolio Margin" tradingPermissions="Stocks,Options,Warrants,Forex,Futures,Crypto Currencies,Mutual Funds,Fully Paid Stock Loan" />
44                    <NetStockPositionSummary>
45                        <NetStockPosition accountId="U1234567" currency="USD" assetCategory="STK" symbol="GRPN" conid="426480582" listingExchange="NASDAQ" netShares="3000" />
46                        <NetStockPosition accountId="U1234567" currency="USD" assetCategory="STK" symbol="META" conid="107113386" listingExchange="NASDAQ" netShares="800" />
47                        <NetStockPosition accountId="U1234567" currency="USD" assetCategory="STK" symbol="NFLX" conid="15124833" listingExchange="NASDAQ" netShares="400" />
48                        <NetStockPosition accountId="U1234567" currency="USD" assetCategory="STK" symbol="PLTR" conid="444857009" listingExchange="NASDAQ" netShares="3100" />
49                        <NetStockPosition accountId="U1234567" currency="USD" assetCategory="STK" symbol="TQQQ" conid="72539702" listingExchange="NASDAQ" netShares="34100" />
50                        <NetStockPosition accountId="U1234567" currency="USD" assetCategory="STK" symbol="TSLA" conid="76792991" listingExchange="NASDAQ" netShares="1500" />
51                        <NetStockPosition 
52                            accountId="U1234567"
53                            currency="USD"
54                            assetCategory="STK"
55                            symbol="TTWO"
56                            conid="6478131"
57                            listingExchange="NASDAQ"
58                            netShares="500" />
59                    </NetStockPositionSummary>
60                </FlexStatement>
61            </FlexStatements>
62         </FlexQueryResponse>
63        "##;
64
65    #[test]
66    fn net_stock_positions_parse() -> Result<()> {
67        let statements = Parser::new()?.parse_flex_query_response(PARTIAL_STATEMENT_EXAMPLE)?;
68        assert_eq!(statements.len(), 1);
69        let result = &statements[0];
70
71        // Ensure we got two equity summaries.
72        assert_eq!(result.net_stock_positions.len(), 7);
73
74        assert_eq!(
75            result.net_stock_positions[6],
76            NetStockPosition {
77                account_id: "U1234567".to_string(),
78                asset_category: AssetCategory::Stock,
79                conid: 6478131,
80                currency: Currency::USD,
81                listing_exchange: "NASDAQ".to_string(),
82                net_shares: 500.0,
83                ticker: "TTWO".to_string(),
84            }
85        );
86        Ok(())
87    }
88}