You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Merge: read per-account data from v1/<alias>/, include transfers and
corporate actions in MergedData, scope trade dedup keys by account.
Tax lots: group FIFO by (account_id, symbol) so each account's lots
are independent. Add TransfersToSyntheticTrades and
TradeTransfersToSyntheticTrades to convert transfer-in records to buy
trades for FIFO. Add AccountAlias to UnmatchedSell and
PositionDiscrepancy. VerifyPositions compares per (account, symbol).
Holdings: combined view aggregates computed positions across accounts
by symbol with weighted-average cost basis. Passes transfers and
trade transfers through to tax lot computation.
README: document new Flex Query sections (Transfers, Trade Transfers,
Corporate Actions), accounts config section, per-account data storage,
multi-account support section, updated probe command.
Copy file name to clipboardExpand all lines: README.md
+49-19Lines changed: 49 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# ibctl
2
2
3
-
A CLI tool for analyzing Interactive Brokers (IBKR) holdings and trades. Downloads data via the IBKR Flex Query API, computes FIFO tax lots, and displays holdings with average prices and positions.
3
+
A CLI tool for analyzing Interactive Brokers (IBKR) holdings and trades. Downloads data via the IBKR Flex Query API, computes FIFO tax lots, and displays holdings with average prices and positions. Supports multiple IBKR accounts with per-account data storage.
4
4
5
5
## Prerequisites
6
6
@@ -17,10 +17,13 @@ Follow these exact steps in the IBKR portal to create a Flex Query and generate
17
17
2. Navigate to **Performance & Reports** > **Flex Queries**.
18
18
3. In the **Activity Flex Query** section, click the **+** button in the top right corner of the panel.
19
19
4. Set the **Query Name** to something descriptive (e.g., "ibctl").
20
-
5. Under **Sections**, add the following three sections, selecting all fields for each:
20
+
5. Under **Sections**, add the following sections, selecting all fields for each:
21
21
-**Trades**
22
22
-**Open Positions**
23
23
-**Cash Transactions** (used for FX rate extraction)
24
+
-**Transfers (ACATS, Internal)** (captures positions transferred from other brokers)
25
+
-**Incoming/Outgoing Trade Transfers** (preserves cost basis and holding period for transferred positions)
-**Period**: `Last 365 Calendar Days` (this is the maximum; see note below about older history)
@@ -49,7 +52,7 @@ Follow these exact steps in the IBKR portal to create a Flex Query and generate
49
52
| Path | Purpose | Override |
50
53
|------|---------|----------|
51
54
|`ibctl.yaml`| Configuration file in current directory |`--config` flag |
52
-
|`<data_dir>/v1/`|Downloaded data cache | Set `data_dir` in config |
55
+
|`<data_dir>/v1/<account>/`|Per-account downloaded data cache | Set `data_dir` and `accounts` in config |
53
56
54
57
## Environment Variables
55
58
@@ -65,7 +68,7 @@ Follow these exact steps in the IBKR portal to create a Flex Query and generate
65
68
ibctl config init
66
69
```
67
70
68
-
This creates a new `config.yaml` in the configuration directory and prints the file path. Edit it to fill in your Flex Query ID and optional symbol classifications.
71
+
This creates a new `ibctl.yaml` in the current directory. Edit it to fill in your Flex Query ID, account mappings, and optional symbol classifications.
69
72
70
73
### Edit Configuration
71
74
@@ -84,6 +87,14 @@ version: v1
84
87
data_dir: ~/Documents/ibctl
85
88
# The Flex Query ID (required, visible next to your query in IBKR portal).
86
89
flex_query_id: "123456"
90
+
# Account aliases mapping (required).
91
+
# Maps user-chosen aliases to IBKR account IDs.
92
+
# Account numbers are confidential — aliases are used in output and directory names.
93
+
# Aliases must be lowercase alphanumeric with hyphens.
# View holdings overview (downloads data automatically if not cached).
125
+
# View combined holdings overview (downloads data automatically if not cached).
115
126
ibctl holdings overview
116
127
ibctl holdings overview --format csv
117
128
ibctl holdings overview --format json
118
129
119
-
# Force re-download of IBKR data.
130
+
# Force re-download of IBKR data (all accounts).
120
131
ibctl download
132
+
133
+
# Probe the API to see what data is available per account.
134
+
ibctl probe
121
135
```
122
136
123
-
Data is downloaded automatically when commands need it. Use `ibctl download` to force a refresh. Each download merges new data with the existing cache — trades are deduplicated by trade ID, so it is safe to run repeatedly.
137
+
Data is downloaded automatically when commands need it. Use `ibctl download` to force a refresh. Each download merges new data with the existing cache — trades are deduplicated by trade ID, so it is safe to run repeatedly. Data is stored per account under `<data_dir>/v1/<account_alias>/`.
124
138
125
139
## Commands
126
140
@@ -130,7 +144,8 @@ Data is downloaded automatically when commands need it. Use `ibctl download` to
130
144
|`ibctl config edit`| Edit the configuration file in `$EDITOR`|
131
145
|`ibctl config validate`| Validate the configuration file |
132
146
|`ibctl download`| Force re-download and cache IBKR data via Flex Query API |
133
-
|`ibctl holdings overview`| Display holdings with prices, positions, and classifications |
147
+
|`ibctl holdings overview`| Display combined holdings with prices, positions, and classifications |
148
+
|`ibctl probe`| Probe the API and show per-account data counts |
134
149
135
150
## Seeding Historical Data
136
151
@@ -143,11 +158,11 @@ IBKR limits all data access (API and portal) to 365 days per request. To get you
143
158
mkdir -p ~/Documents/ibkr-statements
144
159
```
145
160
146
-
2. Create a subdirectory for each IBKR account:
161
+
2. Create a subdirectory for each IBKR account using your aliases:
147
162
```bash
148
-
mkdir ~/Documents/ibkr-statements/RRSP
149
-
mkdir ~/Documents/ibkr-statements/HoldCo
150
-
mkdir ~/Documents/ibkr-statements/Individual
163
+
mkdir ~/Documents/ibkr-statements/rrsp
164
+
mkdir ~/Documents/ibkr-statements/holdco
165
+
mkdir ~/Documents/ibkr-statements/individual
151
166
```
152
167
153
168
3. For each account, log in to [IBKR Account Management](https://www.interactivebrokers.com/portal).
@@ -178,20 +193,35 @@ IBKR limits all data access (API and portal) to 365 days per request. To get you
178
193
The Activity Statement CSVs are **seed data** that you manage. ibctl never modifies them. At command time, ibctl:
179
194
180
195
1. Reads all CSVs from the configured directory (trades, positions, dividends, interest, instrument info)
181
-
2. Reads any cached Flex Query data (from `ibctl download`)
182
-
3. Merges and deduplicates — Flex Query data takes precedence for overlapping trades
183
-
4. Computes tax lots, positions, and holdings from the merged data
196
+
2. Reads any cached Flex Query data per account (from `ibctl download`)
197
+
3. Merges and deduplicates — CSV data takes precedence for overlapping trades
198
+
4. Converts transfers (ACATS) to synthetic trades for FIFO processing
199
+
5. Computes tax lots, positions, and holdings from the merged data
184
200
185
201
To keep data current, the Flex Query API provides the latest 365 days. To add older history, download more CSVs.
186
202
203
+
## Multi-Account Support
204
+
205
+
ibctl supports multiple IBKR accounts via the `accounts` section in the config. Each account is identified by an alias (e.g., "rrsp", "holdco") that maps to an IBKR account ID.
206
+
207
+
-**Downloaded data** is stored per account under `<data_dir>/v1/<alias>/`
208
+
-**Holdings overview** shows a combined view aggregated across all accounts
209
+
-**Transfers** between accounts and from other brokers (ACATS) are tracked and converted to synthetic trades for accurate FIFO computation
210
+
-**Corporate actions** (stock splits, mergers, spinoffs) are captured from the Flex Query API
211
+
212
+
Account numbers are confidential — only aliases appear in output and directory names.
213
+
187
214
## Data Storage
188
215
189
-
Raw API data is cached as protobuf-JSON files under the data directory (`<data_dir>/v1/` as configured in `ibctl.yaml`). Each file stores newline-separated proto JSON (one message per line), serialized using `protojson` with proto field names. Tax lots and derived computations are performed at read time from the merged data (Activity Statement CSVs + cached API data).
216
+
Raw API data is cached as protobuf-JSON files under per-account directories (`<data_dir>/v1/<alias>/`). Each file stores newline-separated proto JSON (one message per line), serialized using `protojson` with proto field names. Tax lots and derived computations are performed at read time from the merged data (Activity Statement CSVs + cached API data).
190
217
191
218
| File | Protobuf Message | Description |
192
219
|------|-----------------|-------------|
193
-
|`trades.json`|`ibctl.data.v1.Trade`| All trades from the IBKR Flex Query. Each trade includes trade ID, dates, symbol, side (buy/sell), quantity, price, proceeds, commission, currency code, and FIFO realized P&L. |
194
-
|`positions.json`|`ibctl.data.v1.Position`| Open positions as reported by IBKR, including quantity, cost basis price, market price, market value, currency code, and unrealized P&L. |
195
-
|`exchange_rates.json`|`ibctl.data.v1.ExchangeRate`| Currency exchange rates with date, base/quote currency codes, rate (units + micros), and provider (ibkr or [frankfurter.dev](https://frankfurter.dev)). |
220
+
|`<alias>/trades.json`|`ibctl.data.v1.Trade`| All trades for this account. Includes trade ID, account, dates, symbol, side (buy/sell), quantity, price, proceeds, commission, currency code, and FIFO realized P&L. |
221
+
|`<alias>/positions.json`|`ibctl.data.v1.Position`| Open positions for this account, including quantity, cost basis price, market price, market value, currency code, and unrealized P&L. |
222
+
|`<alias>/transfers.json`|`ibctl.data.v1.Transfer`| Position transfers (ACATS, ATON, FOP, internal) for this account. Transfer-in records are converted to synthetic buy trades for FIFO processing. |
223
+
|`<alias>/trade_transfers.json`|`ibctl.data.v1.TradeTransfer`| Transferred trade cost basis records. Preserves original trade date and cost basis for positions transferred from other brokers. |
224
+
|`<alias>/corporate_actions.json`|`ibctl.data.v1.CorporateAction`| Corporate action events (stock splits, mergers, spinoffs) for this account. |
225
+
|`exchange_rates.json`|`ibctl.data.v1.ExchangeRate`| Currency exchange rates (shared across accounts) with date, base/quote currency codes, rate, and provider (ibkr or [frankfurter.dev](https://frankfurter.dev)). |
196
226
197
227
Monetary values use `standard.money.v1.Money` with units and micros (6 decimal places). Dates use `standard.time.v1.Date` with year, month, and day fields.
0 commit comments