Note
This is a maintained fork of csingley/ibflex.
The original package on PyPI (v0.15) was last updated in January 2021 and
crashes on modern Interactive Brokers Flex reports. ibflex2 ships
continuously updated dataclasses, an opt-in mode for IBKR's frequent
schema additions, and security and quality hardening.
Install with pip install ibflex2.
ibflex2 converts Interactive Brokers Flex XML brokerage statement data
into typed Python objects, so it can be processed and analyzed in scripts,
notebooks, and pipelines.
This module is for reading IBKR brokerage reports — it does not place trades.
Supports Python 3.9-3.13. The parser has no required dependency beyond
defusedxml; the optional
client for fetching Flex statements depends on
requests (install ibflex2[web]).
- Parses the current IBKR Flex XML format. Many fields and several whole
sections (e.g.
StockGrantActivity) added since 2021. - Opt-in unknown-attribute tolerance so brand-new IBKR fields don't raise. See Tolerance below.
- Security: parses XML through
defusedxmlto guard against XXE, billion-laughs, and external-entity attacks. - Maintained tooling:
pyproject.toml,ruff,mypy,pytestmatrix on Linux/macOS/Windows × Python 3.9-3.13. Coverage gate at 95% (currently 100% on every module exceptutils.py). - Schema-drift CI: a weekly job scrapes the IBKR Activity Flex Query
Reference and opens a tracking issue when fields appear that aren't
declared in
ibflex.Types.
If the original csingley/ibflex package resumes releases, this fork
will be retired. See CHANGELOG.md for what's changed.
pip install ibflex2 # parser only pip install ibflex2[web] # parser + flexget download client
ibflex.parser parses Flex-format XML data into a hierarchy of Python
objects whose structure matches the original Flex statements, with values
converted to Python types (datetime.datetime, decimal.Decimal, etc.)
from ibflex import parser
response = parser.parse("2025-01_ibkr.xml")
# or:
response = parser.parse(xml_bytes)
statement = response.FlexStatements[0]
for trade in statement.Trades:
print(f"{trade.tradeDate} {trade.buySell.name} "
f"{abs(trade.quantity)} {trade.symbol} @ {trade.tradePrice} "
f"{trade.currency}")Every Flex section is exposed as a Tuple of dataclass instances, e.g.
statement.OpenPositions, statement.CashTransactions,
statement.Trades. See ibflex/Types.py for every field on every
dataclass.
IBKR adds new attributes to existing Flex sections regularly. To avoid
FlexParserError when a brand-new attribute appears in your reports,
opt into tolerance mode:
from ibflex import parser
# Process-wide:
parser.enable_unknown_attribute_tolerance()
response = parser.parse("recent_report.xml")
parser.disable_unknown_attribute_tolerance()
# Or scope-limited (recommended):
with parser.unknown_attribute_tolerance():
response = parser.parse("recent_report.xml")Unknown attributes and unknown element types are silently skipped. Known attributes still parse normally. The flag is module-global and not thread-safe; use a single tolerance setting per process or guard with locking if needed.
If you find an unknown attribute that should be recognized, please open a new-field issue with the IBKR field name and an example XML snippet.
Configure Flex statements through Interactive Brokers account management: Reports > Flex Queries > Custom Flex Queries > Configure.
You can configure whatever you like and ibflex2 should parse it, with
these caveats:
- Use ISO-8601 date format (
yyyy-MM-dd) or the defaultyyyyMMdd. European-styledd/MM/yyyyis not supported. - Use a delimiter between dates and times. The default semicolon is fastest to process.
- For Trades, do not select the top options "Symbol Summary", "Asset Class", or "Orders". (You can check the "Asset Class" box further down in the XML attribute list — that's fine.)
Once you've defined Flex queries, you can generate an access token under Reports > Settings > FlexWeb Service. With the token and the query ID you can download statements over HTTP:
from ibflex import client, parser
statement_xml = client.download(token="111111111111111111111111",
query_id="111111")
response = parser.parse(statement_xml)You can also run the bundled flexget script:
flexget -t 111111111111111111111111 -q 111111 > 2025-01_ibkr.xml
The client retries on transient Server busy (5 s backoff) and
Throttled (10 s backoff) responses, and raises
StatementGenerationTimeout after max_tries (default 5).
With Nix (matches CI exactly):
nix develop pytest --cov=ibflex --cov-fail-under=95 tests/ mypy ibflex tests ruff check ibflex tests
Without Nix:
pip install -e .[web,dev] pytest tests/ mypy ibflex tests ruff check ibflex tests
See CONTRIBUTING.md for the new-field workflow, fixture conventions, and release process.
- IBKR Activity Flex Query Reference
- IBKR FlexWeb Service Reference
- capgains — calculates realized gains using ibflex
- ib-flex-analyzer — analyze Flex reports with pandas