Skip to content

Add support for XTB V2 cash operations export format (2025+)#327

Open
WarpPL wants to merge 11 commits into
dickwolff:mainfrom
WarpPL:feat/XTB-new-XLSX-export
Open

Add support for XTB V2 cash operations export format (2025+)#327
WarpPL wants to merge 11 commits into
dickwolff:mainfrom
WarpPL:feat/XTB-new-XLSX-export

Conversation

@WarpPL

@WarpPL WarpPL commented Apr 21, 2026

Copy link
Copy Markdown

Add support for XTB V2 export format (2025+)

Description

This PR adds support for the new XTB V2 "Cash operations" export format.

Changes

  • Auto-detection support in Docker/Watcher mode
    • Added header mapping for XTB V2 format
  • Updated export instructions (README.md)

Checklist

  • Added relevant changes to README (if applicable)
  • Added relevant test(s)
  • Updated the GitVersion file (if not done automatically)

WarpPL added 8 commits April 19, 2026 19:32
* Update cache folder path to prevent state sharing between parallel Jest workers.
* Adjust test setup to use the new cache path.
- Support new 4-line metadata preamble and updated column order
- Auto-detect in watcher via 'Type;Ticker;Instrument' header line
- Normalise XTB exchange suffixes (.UK→.L, .PL→.WA, etc.) to Yahoo format
- Fold withholding tax as fee into adjacent dividend
- Parse European decimal notation (comma, dot thousands separator)
- Handle IKE/IKZE accounts with PLN-only cash currency
- Real Yahoo Finance mock data for 3 new securities
@WarpPL WarpPL requested a review from dickwolff as a code owner April 21, 2026 18:29
@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@WarpPL has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 47 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2f18be0b-3c56-40cd-bee5-394010a7d396

📥 Commits

Reviewing files that changed from the base of the PR and between ca0a518 and 673c935.

📒 Files selected for processing (2)
  • README.md
  • src/converters/xtbConverterV2.test.ts

Walkthrough

This PR adds support for the XTB V2+ cash-operations CSV format by introducing a new XtbConverterV2 class and XtbV2Record model, extensive Jest tests for the converter, and small integrations: registering the new converter in src/converter.ts and src/watcher.ts, updating test environment cache paths for Jest workers, and expanding README instructions to cover both new (xtb-v2) and legacy XTB export workflows.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main objective: adding XTB V2 cash operations export format support for 2025+.
Description check ✅ Passed The description covers the main changes and checks all required template items, though it uses a custom format instead of the exact template structure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
README.md (1)

337-337: ⚠️ Potential issue | 🟡 Minor

Document the new manual converter command.

The XTB instructions reference xtb-v2, but the run-command table still only shows xtb, so local users may run the legacy converter by mistake.

📝 Proposed table update
-| XTB           | `run start xtb`                     |
+| XTB           | `run start xtb` or `run start xtb-v2` |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 337, Update the README run-command table so the XTB entry
reflects the new manual converter command by replacing or augmenting the current
`XTB | run start xtb` entry to reference `run start xtb-v2` (or include both
`xtb` and `xtb-v2` with a note). Locate the table row that uses the symbol "XTB"
and change the command cell to include `run start xtb-v2` (or `run start xtb /
run start xtb-v2` if preserving legacy) and add a short clarifying note
indicating which is the legacy vs. new converter.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@README.md`:
- Around line 197-203: Update the README instructions for exporting the "Cash
operations" sheet to remove the contradictory "Comma-delimited" phrase:
explicitly instruct users to save as "CSV UTF-8 (Semicolon-delimited)" (or "CSV
UTF-8" and ensure the column separator is semicolon) and clarify that the
resulting CSV must use `;` as the delimiter for the xtb-v2 converter; modify the
steps around saving so step 2 uses the corrected label and step 3 simply
reiterates to set the column separator to semicolon (`;`) to avoid confusion.

In `@src/converters/xtbConverterV2.test.ts`:
- Around line 392-405: The test "should use PLN currency for all activity types
in IKE account" is a no-op and must either assert PLN behavior or be removed;
update the test that constructs XtbConverterV2 and calls readAndProcessFile to
either (a) use an input that triggers IKE detection (or call
sut.detectAccountCurrency with a filename indicating IKE) and then assert the
resulting GhostfolioExport (returned to the readAndProcessFile success callback)
has account.currency === 'PLN' and that all activities/transactions in the
export have currency === 'PLN', or (b) if you cannot simulate IKE here, delete
the test. Ensure you modify the test referencing
XtbConverterV2.readAndProcessFile and GhostfolioExport to include these explicit
assertions (or remove the test entirely).

In `@src/converters/xtbConverterV2.ts`:
- Around line 54-62: The detectAccountCurrency function currently treats any
three-letter prefix as a currency; update detectAccountCurrency (use the
existing filename and marker variables) to validate the detected three-letter
marker against a whitelist/set of valid ISO currency codes (e.g., EUR, USD, GBP,
PLN, etc.) before returning it; if marker is "IKE" or "IKZE" keep returning
"PLN", if marker exists but is not in the valid-currencies set fall back to
process.env.XTB_ACCOUNT_CURRENCY || "EUR" so arbitrary prefixes like "XTB" are
not accepted as currencies.
- Around line 238-265: When divCurrency matches the account currency, change the
dividend export to use the parsed per-share rate and derived quantity instead of
always using quantity=1 and unitPrice=abs(record.amount); specifically, after
obtaining security from securityService.getSecurity and extracting the parsed
dividend rate (from divMatch or the security record), compute quantity =
Math.abs(record.amount) / dividendRate (apply safe numeric parsing and rounding
as appropriate) and set unitPrice = dividendRate; keep the existing behavior
(quantity=1, unitPrice=abs(record.amount)) as a fallback when currencies do not
match or the dividendRate is missing, and ensure you still supply feeAmount,
comment, accountId and GhostfolioOrderType["dividend"] in the result.activities
entry.
- Around line 127-143: The dayjs parsing in XtbConverterV2 (the constructor and
the date parse at record.time) is missing the customParseFormat and utc plugins
and thus fails to parse "YYYY-MM-DD HH:mm:ss" as UTC; update the XtbConverterV2
constructor to import and extend dayjs with customParseFormat and utc, then
change the parse call to use dayjs.utc(record.time.trim(), "YYYY-MM-DD
HH:mm:ss") (or equivalent) so date handling is robust and consistent across
hosts; ensure references to dayjs.extend, the XtbConverterV2 constructor, and
the parsing site (where date = dayjs(...)) are updated accordingly.

---

Outside diff comments:
In `@README.md`:
- Line 337: Update the README run-command table so the XTB entry reflects the
new manual converter command by replacing or augmenting the current `XTB | run
start xtb` entry to reference `run start xtb-v2` (or include both `xtb` and
`xtb-v2` with a note). Locate the table row that uses the symbol "XTB" and
change the command cell to include `run start xtb-v2` (or `run start xtb / run
start xtb-v2` if preserving legacy) and add a short clarifying note indicating
which is the legacy vs. new converter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 63c3252e-b41e-42c3-9493-53f3e30abf1f

📥 Commits

Reviewing files that changed from the base of the PR and between 9e35bd6 and 0289d3e.

⛔ Files ignored due to path filters (2)
  • assets/export-xtb-1.jpg is excluded by !**/*.jpg
  • samples/xtb-v2-export.csv is excluded by !**/*.csv
📒 Files selected for processing (11)
  • GitVersion.yml
  • README.md
  • src/converter.ts
  • src/converters/xtbConverterV2.test.ts
  • src/converters/xtbConverterV2.ts
  • src/models/xtbV2Record.ts
  • src/securityService.test.ts
  • src/testing/data/yahooFinanceQuoteSummaryResults.json
  • src/testing/data/yahooFinanceSearchResults.json
  • src/testing/testEnvVars.js
  • src/watcher.ts

Comment thread README.md
Comment thread src/converters/xtbConverterV2.test.ts Outdated
Comment thread src/converters/xtbConverterV2.ts
Comment thread src/converters/xtbConverterV2.ts
Comment thread src/converters/xtbConverterV2.ts Outdated
@WarpPL WarpPL changed the title XTB new XLSX export Add support for XTB V2 cash operations export format (2025+) Apr 21, 2026
@WarpPL

WarpPL commented Apr 21, 2026

Copy link
Copy Markdown
Author

@coderabbitai autofix

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

❌ Failed to clone repository into sandbox. Please try again.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/converters/xtbConverterV2.test.ts (1)

392-395: ⚠️ Potential issue | 🟡 Minor

Test name does not match what is being asserted, and duplicates the detectAccountCurrency suite.

The it says "should use PLN currency for all activity types in IKE account", but the body only calls XtbConverterV2.detectAccountCurrency(...) against two filenames — both of which are already covered by returns PLN for XTB_IKE_ filename (line 207-209) and the IKZE variants in the detectAccountCurrency describe block. To honor the original intent, drive processFileContents (or readAndProcessFile with an IKE-named fixture) and assert that every activity in the resulting export has currency === "PLN", similar to the EUR-account test at lines 338-390. Otherwise, drop the test as redundant.

🧪 Suggested rewrite to match the test name
-    it("should use PLN currency for all activity types in IKE account", () => {
-        expect(XtbConverterV2.detectAccountCurrency("XTB_IKE_99000009_2006-01-01_2026-04-19.csv")).toBe("PLN");
-        expect(XtbConverterV2.detectAccountCurrency("XTB_IKZE_99000009_2006-01-01_2026-04-19.csv")).toBe("PLN");
-    });
+    it("should use PLN currency for all activity types in IKE account", (done) => {
+
+        const sut = new XtbConverterV2(new SecurityService(new YahooFinanceServiceMock()));
+
+        let csv = "";
+        csv += "Account number;99000001;;;;;;\n";
+        csv += "Cash Operations;;;;;;;\n";
+        csv += "Date from (UTC);2006-01-01 00:00:00;;;;;;\n";
+        csv += "Date to (UTC);2026-04-19 14:07:32;;;;;;\n";
+        csv += "Type;Ticker;Instrument;Time;Amount;ID;Comment;Product\n";
+        csv += "Free funds interest;;;2026-04-03 16:04:18;0,02;200001;Free-funds Interest 2026-03;IKE\n";
+        csv += "Stock purchase;IS0M.DE;Italy Govt Bond;2026-03-02 13:31:07;-2154,74;200003;OPEN BUY 14 @ 153.91;IKE\n";
+
+        // Use a filename that triggers IKE detection.
+        sut.processFileContents(csv, (actualExport: GhostfolioExport) => {
+            actualExport.activities.forEach(a => expect(a.currency).toBe("PLN"));
+            done();
+        }, () => done.fail("Should not have an error!"),
+        /* fileName: */ "XTB_IKE_99000009_2006-01-01_2026-04-19.csv");
+    });

Note: the optional fileName argument above assumes processFileContents accepts one — please verify against the converter signature.

#!/bin/bash
# Confirm processFileContents signature so the suggested fixture-driven test compiles.
ast-grep --pattern $'processFileContents($$$) {
  $$$
}'
rg -nP -C2 '\bprocessFileContents\s*\(' --type=ts
🧹 Nitpick comments (1)
src/converters/xtbConverterV2.test.ts (1)

318-318: Avoid (sut as any) to reach a private member; expose a seam instead.

jest.spyOn((sut as any).progress, "log") reaches into a private field and silently couples the test to the internal property name. Renaming progress in the converter would break this test at runtime with no compile-time signal. Consider one of:

  • Spying on the underlying console/logger that progress ultimately writes to.
  • Adding a protected getter or a constructor-injectable progress logger so tests can assert against it through the public API.

Not blocking, but it’s a brittle seam in an otherwise clean test file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/converters/xtbConverterV2.test.ts` at line 318, The test currently
reaches into a private field with jest.spyOn((sut as any).progress, "log"),
which couples tests to an internal name; instead modify the converter to expose
a seam and change the test to spy through that seam: either (A) add a
constructor parameter to inject a progress logger (e.g., accept a ProgressLogger
in the converter constructor) and in tests pass a mock/spied logger and use
jest.spyOn(mockLogger, "log"), or (B) add a protected getter like getProgress()
that returns the progress logger and have the test call
jest.spyOn(sut.getProgress(), "log") (or subclass in tests to override it);
update the test to remove the (sut as any) cast and spy on the
injected/getter-provided logger so the test no longer depends on the private
progress property name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/converters/xtbConverterV2.test.ts`:
- Around line 216-230: The test name for the spec using
XtbConverterV2.readAndProcessFile is misleading: it says "should use PLN
currency for INTEREST when reading IKE file" but uses
"samples/xtb-v2-export.csv" and asserts interest.currency === "EUR"; either
rename the it(...) description to reflect the EUR-default behavior being tested,
or change the fixture/filename passed to readAndProcessFile to a sample that
triggers IKE/PLN detection so the assertion for PLN is valid; update the it(...)
title string accordingly and keep the rest of the test (interest lookup and
expectation) consistent with the chosen fixture.

---

Nitpick comments:
In `@src/converters/xtbConverterV2.test.ts`:
- Line 318: The test currently reaches into a private field with jest.spyOn((sut
as any).progress, "log"), which couples tests to an internal name; instead
modify the converter to expose a seam and change the test to spy through that
seam: either (A) add a constructor parameter to inject a progress logger (e.g.,
accept a ProgressLogger in the converter constructor) and in tests pass a
mock/spied logger and use jest.spyOn(mockLogger, "log"), or (B) add a protected
getter like getProgress() that returns the progress logger and have the test
call jest.spyOn(sut.getProgress(), "log") (or subclass in tests to override it);
update the test to remove the (sut as any) cast and spy on the
injected/getter-provided logger so the test no longer depends on the private
progress property name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 423f67cd-730f-4835-9e2d-0bd6bec7aaf7

📥 Commits

Reviewing files that changed from the base of the PR and between 0289d3e and ca0a518.

📒 Files selected for processing (3)
  • README.md
  • src/converters/xtbConverterV2.test.ts
  • src/converters/xtbConverterV2.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/converters/xtbConverterV2.ts

Comment thread src/converters/xtbConverterV2.test.ts Outdated
@WarpPL

WarpPL commented Apr 27, 2026

Copy link
Copy Markdown
Author

Hi @dickwolff !
I’ve improved the exports for Degiro (V3 - #326) and XTB (V2 - this one). Could you please take a quick look at the PRs when you have a moment?

@WarpPL

WarpPL commented May 23, 2026

Copy link
Copy Markdown
Author

Hi @dickwolff !
I wanted to follow up on this PR, as it’s quite important for my use case and currently blocked on review. I also noticed there are quite a few pending PRs overall.
Would you be able to take a look, or let me know if there’s anything I can adjust to help move this forward?

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.

1 participant