|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | | -from dataclasses import dataclass |
| 3 | +from types import SimpleNamespace |
4 | 4 | import unittest |
5 | 5 |
|
6 | 6 | from quant_platform_kit.ibkr.portfolio import fetch_portfolio_snapshot |
7 | 7 |
|
8 | 8 |
|
9 | | -@dataclass |
10 | | -class FakeContract: |
11 | | - symbol: str |
12 | | - |
13 | | - |
14 | | -@dataclass |
15 | | -class FakePosition: |
16 | | - contract: FakeContract |
17 | | - position: int |
18 | | - avgCost: float |
19 | | - |
20 | | - |
21 | | -@dataclass |
22 | | -class FakeAccountValue: |
23 | | - tag: str |
24 | | - currency: str |
25 | | - value: str |
26 | | - |
27 | | - |
28 | 9 | class FakeIB: |
| 10 | + def __init__(self): |
| 11 | + self.req_positions_called = False |
| 12 | + |
29 | 13 | def reqPositions(self): |
30 | | - self.positions_requested = True |
| 14 | + self.req_positions_called = True |
31 | 15 |
|
32 | 16 | def positions(self): |
33 | 17 | return [ |
34 | | - FakePosition(contract=FakeContract("SPY"), position=10, avgCost=99.0), |
35 | | - FakePosition(contract=FakeContract("AGG"), position=0, avgCost=100.0), |
| 18 | + SimpleNamespace( |
| 19 | + account="U18308207", |
| 20 | + contract=SimpleNamespace(symbol="TQQQ"), |
| 21 | + position=3, |
| 22 | + avgCost=100.0, |
| 23 | + ), |
| 24 | + SimpleNamespace( |
| 25 | + account="U15998061", |
| 26 | + contract=SimpleNamespace(symbol="AAPL"), |
| 27 | + position=5, |
| 28 | + avgCost=200.0, |
| 29 | + ), |
36 | 30 | ] |
37 | 31 |
|
38 | 32 | def accountValues(self): |
39 | 33 | return [ |
40 | | - FakeAccountValue(tag="NetLiquidation", currency="USD", value="100000"), |
41 | | - FakeAccountValue(tag="AvailableFunds", currency="USD", value="25000"), |
| 34 | + SimpleNamespace(account="U18308207", tag="NetLiquidation", currency="USD", value="1000"), |
| 35 | + SimpleNamespace(account="U18308207", tag="AvailableFunds", currency="USD", value="250"), |
| 36 | + SimpleNamespace(account="U15998061", tag="NetLiquidation", currency="USD", value="2000"), |
| 37 | + SimpleNamespace(account="U15998061", tag="AvailableFunds", currency="USD", value="500"), |
42 | 38 | ] |
43 | 39 |
|
44 | 40 |
|
45 | 41 | class IbkrPortfolioTests(unittest.TestCase): |
46 | | - def test_fetch_portfolio_snapshot_returns_equity_and_positions(self) -> None: |
47 | | - snapshot = fetch_portfolio_snapshot(FakeIB(), wait_seconds=0) |
48 | | - |
49 | | - self.assertEqual(snapshot.total_equity, 100000.0) |
50 | | - self.assertEqual(snapshot.buying_power, 25000.0) |
51 | | - self.assertEqual(len(snapshot.positions), 1) |
52 | | - self.assertEqual(snapshot.positions[0].symbol, "SPY") |
53 | | - self.assertEqual(snapshot.positions[0].market_value, 990.0) |
| 42 | + def test_fetch_portfolio_snapshot_filters_by_account_id(self) -> None: |
| 43 | + ib = FakeIB() |
| 44 | + |
| 45 | + snapshot = fetch_portfolio_snapshot(ib, account_ids=("U18308207",), wait_seconds=0) |
| 46 | + |
| 47 | + self.assertTrue(ib.req_positions_called) |
| 48 | + self.assertEqual(snapshot.total_equity, 1000.0) |
| 49 | + self.assertEqual(snapshot.buying_power, 250.0) |
| 50 | + self.assertEqual(tuple(position.symbol for position in snapshot.positions), ("TQQQ",)) |
| 51 | + self.assertEqual(snapshot.positions[0].account_id, "U18308207") |
| 52 | + self.assertEqual(snapshot.metadata["account_ids"], ("U18308207",)) |
| 53 | + |
| 54 | + def test_fetch_portfolio_snapshot_sums_selected_accounts(self) -> None: |
| 55 | + snapshot = fetch_portfolio_snapshot( |
| 56 | + FakeIB(), |
| 57 | + account_ids=("U18308207", "U15998061"), |
| 58 | + wait_seconds=0, |
| 59 | + ) |
| 60 | + |
| 61 | + self.assertEqual(snapshot.total_equity, 3000.0) |
| 62 | + self.assertEqual(snapshot.buying_power, 750.0) |
| 63 | + self.assertEqual(tuple(position.symbol for position in snapshot.positions), ("TQQQ", "AAPL")) |
54 | 64 |
|
55 | 65 |
|
56 | 66 | if __name__ == "__main__": |
|
0 commit comments