A small TypeScript CLI that pulls your IBKR portfolio data through the Flex Web Service — positions, trades, cash, dividends, performance, FX, and anything else a Flex Query can return — and caches it locally in SQLite.
Flex is read-only end-of-day reporting. It gives you executed trades, not live working/open orders. (Open orders would need the Client Portal Web API, which is out of scope here.) Activity data refreshes once daily at close; Trade Confirmation data refreshes intraday with a short delay.
New here? Follow the step-by-step setup guide — it walks through generating the IBKR token, creating Flex Queries, installing, and configuring, with troubleshooting. The sections below are the quick reference.
The Flex Web Service is a two-call flow:
SendRequest?t={token}&q={queryId}&v=3→ returns a reference code.GetStatement?t={token}&q={referenceCode}&v=3→ returns the report XML (the client polls with backoff until it stops returning "generation in progress").
The parser is generic — it auto-discovers every section in the returned XML, so the tool covers all sections without a hand-written parser per section.
-
Enable the service & get a token. Client Portal → Settings → Account Settings → Reporting → Flex Web Service Configuration → generate a token. It expires (~1 year); set a reminder to rotate it.
-
Create the Flex Queries below. Reporting → Flex Queries. Create each one as an Activity Flex Query (or Trade Confirmation Flex Query for
fills), set Format = XML, Period = Last 365 Calendar Days, tick the listed sections, then copy each Query ID.Splitting into several small queries (rather than one giant query) keeps each report fast to generate and avoids timeouts.
Query name Type Sections to include accountActivity Account Information, Financial Instrument Information, Currency Conversion Rate positionsActivity Open Positions, Prior Period Positions, Net Stock Position Summary, Complex Position Summary, Change in Position Value, Pending Exercises tradesActivity Trades, Unbooked Trades, Options/Exercises/Assignments/Expirations, Trade Transfers, Transaction Fees, Commission Details, Routing Commissions cashActivity Cash Report, Cash Transactions, Statement of Funds, Transfers, Unsettled Transfers, Debit Card Activity incomeActivity Corporate Actions, Open/Change in Dividend Accruals, Interest Accruals, Interest Details (Tiers) performanceActivity Change in NAV, NAV Summary, MTM Performance, Realized/Unrealized Performance, Month/YTD Performance fxActivity Forex Balances, Forex P/L Details, Currency Conversion Rate lendingActivity Client Fees, Borrow Fee Details, Securities Borrowed/Lent (+ Activity + Fees), Soft Dollar Activity fillsTrade Confirmation Trade confirmations (intraday fills) You don't have to create all of them on day one. Only queries whose ID you fill into
config.jsonwill be used.
npm install
cp .env.example .env # add IB_FLEX_TOKEN (gitignored, never commit)
cp config.example.json config.json # fill in each Query ID
npm run build # compile to dist/
npm link # optional: put `ib` on your PATHThe ib … examples below assume you ran npm link (or installed globally).
Without it, run any command through tsx instead: npm run dev -- <command>
(e.g. npm run dev -- positions --json). Run npm test to execute the suite.
ib sync --all # fetch every configured query into the cache
ib sync positions # fetch just one
ib sync trades --from 20250101 --to 20250131 # override the query's date range
ib positions # pretty table of holdings + unrealized P/L
ib trades # executed trades
ib cash # deposits / withdrawals / dividends / fees
ib dividends # dividend accruals
ib raw cash # every section a query returns, with row counts (always live)
ib raw cash --full # full parsed JSON (use this to discover real section names)
ib sections # what each configured query should contain
ib prune # drop old cached snapshots, keeping the latest 10 per query
ib prune --keep 3 # keep fewer; bounds the sqlite cache size
# global flags
ib positions --json # JSON instead of a table (pipe into jq, etc.)
ib positions --live # skip cache, fetch fresh
ib --db ./my.db sync --all--from/--to take yyyymmdd and are available on sync and raw; omit
them to use the query's saved period. A daily ib sync --all grows the cache
over time, so run ib prune periodically (it keeps the latest N snapshots per
query and reclaims disk).
Tip: ib raw <query> --full shows the actual XML element names your
account returns. If a friendly view shows "(no rows)", the section's real
element name probably differs from the default — update src/sections.ts and
the section argument in src/cli.ts to match.
- The token is read from the environment only;
config.jsonholds just Query IDs and is safe to commit..env,config.json, and*.dbare gitignored. - The Flex API is read-only — a leaked token can't place trades — but it still
exposes financial PII, so treat the token and the SQLite cache as secrets
(
chmod 600). - Rotate the token before it expires.
- Friendly views for the remaining sections (performance, FX, corporate actions).
- Materialised typed tables for sections you query often.
- Portfolio analytics: yield-on-cost, projected annual income, FX-adjusted total return.
- Optional Client Portal Web API module if live open orders are ever needed.
MIT — see LICENSE.
This is an independent, unofficial tool. It is not affiliated with, endorsed by, or supported by Interactive Brokers. "Interactive Brokers", "IBKR", and "Flex" are trademarks of Interactive Brokers LLC. Provided "as is", with no warranty; it is not financial advice. Always verify figures against your official IBKR statements, and use within Interactive Brokers' API terms.