Skip to content

Import IBKR Flex Query XML uploads in the statement timezone - #45

Merged
ChristopherDownie merged 1 commit into
mainfrom
fix/ibkr-flex-xml-upload
Oct 13, 2026
Merged

ChristopherDownie merged 1 commit into
mainfrom
fix/ibkr-flex-xml-upload

Conversation

@ChristopherDownie

Copy link
Copy Markdown
Contributor

Fixes #16.

Problem

Import → File upload accepts .xml, but no importer reads Flex Query XML. A Flex statement fell through to the column mapper, whose only "column" was the <?xml …?> declaration line, so the file could not be imported and the Default import timezone never applied. PR #24 fixed the broker-sync path; direct XML upload was still unsupported (docs/importers.md said so).

Reproduced on main with the report's fills (dateTime="20260918;085905" / "20260918;100830", NYMEX MCL): /api/import returned detected: null, needsMapping: true.

Fix

  • Uploaded Flex XML is parsed server-side with the broker SDK's parseFlexStatement, the same parser broker sync uses, with the statement timezone from the upload form.
  • Sync and upload now share one SDK-trade → execution mapping (server/broker-trades.ts), so the same fill hashes identically from either path.
  • Safeguards:
    • An upload into an IBKR sync account must use the timezone that account syncs with (recorded, or the default import timezone if nothing has synced yet). A different zone is refused before any writes, because every fill would otherwise be stored twice.
    • A statement that mixes IBKR accounts is refused (the SDK would merge them under the first account).
    • <Trade> rows with a levelOfDetail other than EXECUTION (order/lot summaries) are skipped so fills are not counted twice.
    • Zero-price rows (expiries, assignments) and unreadable timestamps are skipped and reported, with a hint about the supported Flex date format.
    • A UTC offset as the statement timezone returns a 400 instead of a 500.
  • The import page lists Flex XML among auto-detected formats (labels de-duplicated); docs, README and CHANGELOG updated.

Verification

  • New apps/web/tests/ibkr-flex-xml-import.test.ts (17 tests): the report's case stored as 12:59:05Z / 14:08:30Z and shown as 14:59:05 / 16:08:30 in Europe/Rome; the report's UTC / America/New_York / Pacific/Kiritimati matrix; explicit offsets; re-import dedupe; hash parity with synced fills; refusal in another zone; mixed accounts; summary rows; zero-price/unreadable rows; offset timezone; non-Flex XML still goes to the mapper.
  • Full suite 583/583, typecheck, format, license check, package builds and production build pass locally.
  • Checked in the browser with a synthetic statement: the preview and the stored trade show 14:59:05 / 16:08:30 in Rome for a New York statement timezone.

Import → File upload accepted .xml files but no importer read Flex Query XML,
so a Flex statement fell through to the column mapper and could not be
imported. The Default import timezone therefore never applied (#16).

Uploads now use the broker SDK's Flex parser, the same parser broker sync
uses, with the statement timezone from the upload form. Sync and upload share
one SDK-trade mapping, so the same fill hashes identically from either path.

- Skip order/lot summary rows so fills are not counted twice
- Refuse statements that mix IBKR accounts
- Refuse uploads into an IBKR sync account in a timezone other than the one
  it syncs with, which would otherwise store every fill twice
- Report zero-price and untimed rows instead of guessing
- Return a 400, not a 500, for a UTC offset statement timezone

Fixes #16
@ChristopherDownie
ChristopherDownie merged commit 9994711 into main Oct 13, 2026
1 check passed
on101094 added a commit to on101094/trade-journal that referenced this pull request Oct 13, 2026
Resolve the CHANGELOG conflict by keeping both "Fixed" entries: the Net ROI
contract-multiplier line from this branch and the IBKR Flex Query XML line
from upstream LuxAlgo#45.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

"Default import timezone" setting is ignored during import/re-import — execution times always computed as if raw timestamp were UTC

1 participant