Repository navigation
Import XTB xStation .xlsx account workbooks - #46
Merged
Merged
Conversation
XTB exports one workbook per account with Closed Positions, Open Positions and Cash Operations sheets. Closed rows become an entry and an exit fill; open lots become entry fills tagged with their XTB position id so a later workbook that closes the position adds the exit. Cash operations are skipped. Includes a dependency-free .xlsx reader (ZIP + worksheet XML). Contributed in #42.
Review fixes on top of the contributed importer (#42): - Typecheck: give the importers package DOM lib typings for TextDecoder and DecompressionStream, and type tradeToExecutions as an entry/exit pair - Decode workbooks on the server: the page uploads the .xlsx as base64 with encoding "xlsx" (5 MB cap), so preview and commit parse the same server-side text and the reader stays out of the client bundle - Bound the ZIP reader: check every offset, refuse ZIP64, multi-part, encrypted members and legacy .xls with a clear message, cap entries and expanded size, and stop inflating past a member's declared size - Refuse a later close whose lot differs from the stored holding (for example a partial close) instead of booking a phantom open position; tolerate sub-second and rounding differences for the same lot - Open lots inherit the category of their ticker's rollup row, so holdings keep their asset class when a later workbook closes them - Count only unknown trade types as skipped; totals, rollups and notes are layout rows - Replace the real-export fixtures with synthetic workbooks generated in the layout of xStation exports; no account data is committed Fixes #42
ChristopherDownie
force-pushed
the
feat/xtb-xlsx-import
branch
from
October 13, 2026 23:10
b885ec9 to
07cd06d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #42.
Builds on @zahei's contributed importer (first commit, authored by them): XTB exports one
.xlsxper account; closed positions become an entry and an exit fill, open lots become entry fills keyed by XTB position id, and a later workbook that closes the position adds the exit. Cash operations are skipped. The.xlsxreader has no new dependency.Review fixes (second commit)
Running the patch as submitted showed:
pnpm typecheckfails inpackages/importers(no DOM typings forTextDecoder/DecompressionStream; strict index access ontradeToExecutions)tradeToExecutionstyped as an entry/exit pairpackages/importers/tests/xtb-fixture.ts); no account data is committedserver/xtb-import.tschecks incoming XTB entries against stored holdings by position id: rounding-level differences (under 1 s, price within 0.1%) are the same lot and are not inserted again; a changed size or side is refused before any writes, with a message naming the position.xlsxas base64 (encoding: "xlsx", 5 MB cap) and the server reads it; every ZIP offset is bounds-checked; ZIP64, multi-part, encrypted members and legacy.xlsare refused with clear messages; entry count and expanded size are capped and inflation stops past a member's declared sizeVerification
packages/importers/tests/xtb.test.ts(13 tests): closed position with account-currency result, holdings keyed by position id, later close reproducing the holding's entry exactly, fees, mixed accounts, non-XTB workbooks, and reader limits (stored vs deflated,.xls, truncated, declared-size overrun, oversize, encrypted).apps/web/tests/xtb-import.test.ts(5 tests): base64 preview/commit and re-import dedupe; holding closed by a later workbook; rounding drift treated as the same lot; partial close refused with nothing saved; AI/mapping/oversized/non-workbook uploads refused.apps/web/tests/xtb-import-ui.test.ts(2 tests): the page sends base64 withencoding: "xlsx"for preview and commit, and blocks AI parsing and oversized files before any request.Known limits
Partial closes are refused until a real partial-close export shows how XTB reports them. Prices stay in the instrument currency, so a cross-currency position's result is in the account currency but its entry notional is not (documented in
docs/importers.md).Note: this PR and #45 both touch
api/import/route.tsand the README format count; whichever merges second needs a small rebase (the count becomes 16).