Thanks for sending a fix or feature. The maintained fork's job is to keep parsing IBKR Flex Query reports correctly even as IBKR adds new fields and sections faster than upstream releases.
# With Nix (preferred — matches CI)
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 testsThe make test target chains mypy + ruff + pytest.
When IBKR adds an attribute to a Flex section, the type definition needs to catch up. Checklist for a "new field" PR:
- Add the field to the relevant dataclass in
ibflex/Types.py. Keep the field grouped with related ones — don't blindly append at the end. UseOptional[T] = Noneso existing data without the attribute still parses. - Add a test in
tests/test_types.pyusing a real (anonymized) XML snippet from your Flex report. Reuse one of the existing*TestCasepatterns; the test only needs to assert the new attribute parses. - Update
CHANGELOG.mdunder theUnreleased→Addedsection. - If the type changed (e.g.
Optional[Decimal]->Optional[bool]), add an entry underUnreleased→Breaking changesand explain what downstream code might silently misbehave.
- Add the class to
ibflex/Types.pywith@dataclass(frozen=True)and a one-line docstring noting which container element wraps it. - Add the class name to the module's
__all__list (alphabetical). - Wire it into
FlexStatement(replace theTuple = () # TODOplaceholder with the typed tuple). - Add a
*TestCaseintests/test_types.pyusing real anonymized XML. - Update
CHANGELOG.md.
ibflex/enums.py mirrors the IBKR-supplied codes. New values get added
alphabetically (or by code-letter ordering where that's the existing
convention). Add a quick parser test in tests/test_parser.py →
testConvertEnum for the new value, or a full round-trip test in
tests/test_types.py if the value gates parsing of a whole element.
Real Flex reports contain account numbers, names, addresses, and trade history. Before pasting into a test:
- Account IDs →
U123456/U1234567 - Account aliases →
""or"test" - Personal names →
"Test User"etc. - ConIDs and securityIDs are fine (public). Symbols, descriptions are fine.
- Trade IDs, order IDs, exec IDs →
"123","9876543210", etc.
The parser has an opt-in mode for shrugging off unknown attributes:
import ibflex
ibflex.enable_unknown_attribute_tolerance()If a user reports a FlexParserError: NewClass has no attribute newField,
they can use this as an immediate workaround while a fix is in flight. PRs
adding fields should not rely on or modify this feature.
Optional but recommended for any change touching ibflex/parser.py:
pip install mutmut
mutmut run --paths-to-mutate ibflex/parser.py.github/workflows/sync-upstream.yml opens a daily PR from csingley/ibflex
master into our master. When it conflicts:
git fetch upstream
git checkout master
git merge upstream/master
# resolve conflicts; favor keeping ibflex2-specific cleanup (pyproject.toml,
# tolerance feature, etc.)
git push origin masterThe fork's CHANGELOG entry should note any upstream changes that affected public behavior.
- Don't change
Optional[X]annotations toX | Nonesyntax inTypes.pyorparser.py. The parser'sATTRIB_CONVERTERStable is keyed on the string form of the annotation, and PEP 604 syntax silently breaks the dispatch. - Don't switch back to
xml.etree.ElementTreefromdefusedxml. - Don't add fields to
SymbolSummarywithout verifying they don't already exist earlier in the dataclass — past PRs have introduced duplicates from bad merges.
- Bump
ibflex/__version__.__version__. - Move
Unreleasedentries inCHANGELOG.mdunder a dated heading. - Commit and push to master.
- Create a GitHub Release with a tag matching the new version.
publish.ymlbuilds and uploads to PyPI via trusted publishing.