ibkr_flex_statement/
net_stock_position.rs1use 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 FULL_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 result = Parser::new()?.parse_statement_contents(FULL_STATEMENT_EXAMPLE)?;
68
69 assert_eq!(result.net_stock_positions.len(), 7);
71
72 assert_eq!(
73 result.net_stock_positions[6],
74 NetStockPosition {
75 account_id: "U1234567".to_string(),
76 asset_category: AssetCategory::Stock,
77 conid: 6478131,
78 currency: Currency::USD,
79 listing_exchange: "NASDAQ".to_string(),
80 net_shares: 500.0,
81 ticker: "TTWO".to_string(),
82 }
83 );
84 Ok(())
85 }
86}