Skip to content

Read Freetrade dates in UK time - #1010

Merged
kodiakhq[bot] merged 3 commits into
cgt-calc:mainfrom
hdimer:freetrade-uk-dates
Aug 26, 2026
Merged

kodiakhq[bot] merged 3 commits into
cgt-calc:mainfrom
hdimer:freetrade-uk-dates

Conversation

@hdimer

@hdimer hdimer commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #1005.

FreetradeTransaction.__init__ took the calendar date straight from the UTC timestamp, so a transaction stamped after 23:00 UTC on 5 April was filed in the previous tax year, and the same instant could also land on the wrong side of same-day and 30-day matching.

It now converts to UK_TIMEZONE before taking the date, the same way Trading212Transaction has since #1000. As there, a zone-less timestamp is read as UTC rather than as the machine's local time: every export carries Z, but without that the parsed date would depend on the reader's clock, and BASE_ROW_VALUES in the Freetrade tests uses the zone-less shorthand.

The new parametrized test mirrors the Trading 212 one: the 5/6 April boundary (22:59:59 UTC still 5 April, 23:00:00 and 23:30:00 already 6 April, the last of those zone-less), the evening after the clocks go back in October, and two winter dates where UK and UTC agree anyway. I checked each case discriminates by re-running it against the unfixed parser (two boundary cases fail), against a plain astimezone with no zone-less handling under TZ=Pacific/Auckland (the zone-less case fails), and against a crude "add an hour from April to October" version (the October case fails).

The second half of the issue is documented rather than fixed. docs/brokers/freetrade.md gains a "Dates and time zones" section mirroring the Trading 212 one, and docs/brokers/index.md says once that every other export states a date with no time, so cgt-calc reads it as the broker wrote it. That seemed better than repeating the same paragraph on six broker pages, but happy to move it if you would rather each page carried it.

Checks: uv run pytest 904 passed, and the Freetrade tests also pass under TZ set to UTC, Auckland, Los Angeles and Tokyo. Plus ruff format, ruff check, mypy, ty, dprint, codespell and Harper.

Disclosure: written with AI assistance (Claude). The bug was reproduced against main first, and the new test was confirmed to fail on the unfixed parser before anything was changed.

The Freetrade export timestamps every transaction in UTC, and the parser
took the calendar date straight from that instant. The tax year boundary
and the same-day and 30-day matching rules run on UK calendar days, and
5 April always falls inside BST, so anything stamped after 23:00 UTC on
5 April was filed in the previous tax year.

Convert to Europe/London before taking the date, the same way Trading
212 does since cgt-calc#1000. A zone-less timestamp is read as UTC rather than
as the machine's local time, so the parsed date does not depend on where
the calculation is run.

Document the behaviour on the Freetrade page. The date-only exports
(Schwab, Interactive Brokers, Hargreaves Lansdown, Vanguard, Morgan
Stanley, Sharesight and RAW) carry no time to convert, so say so once on
the brokers index rather than on each page.
@github-actions

github-actions Bot commented Aug 25, 2026 •

Copy link
Copy Markdown

📖 Docs preview: https://pr-1010.cgt-calc.pages.dev

Built from e63ee89 · updated 2026-08-26 14:10 BST

@hdimer
hdimer marked this pull request as ready for review August 25, 2026 23:23
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.10%. Comparing base (720743e) to head (fd9bf55).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #1010   +/-   ##
=======================================
  Coverage   94.10%   94.10%           
=======================================
  Files          37       37           
  Lines        4902     4906    +4     
  Branches      810      811    +1     
=======================================
+ Hits         4613     4617    +4     
  Misses        150      150           
  Partials      139      139           
Flag Coverage Δ
docker 93.86% <100.00%> (+<0.01%) ⬆️
python-3.12-macos-latest 93.62% <100.00%> (+<0.01%) ⬆️
python-3.12-ubuntu-latest 93.62% <100.00%> (+<0.01%) ⬆️
python-3.12-windows-latest 93.66% <100.00%> (+<0.01%) ⬆️
python-3.13-ubuntu-latest 93.62% <100.00%> (+<0.01%) ⬆️
python-3.14-ubuntu-latest 93.59% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Core calculation 94.78% <ø> (ø)
Broker parsers 93.62% <100.00%> (+<0.01%) ⬆️
ERI importers 96.48% <ø> (ø)
Files with missing lines Coverage Δ
cgt_calc/parsers/freetrade.py 93.06% <100.00%> (+0.16%) ⬆️

@vmartinv vmartinv added bug Something isn't working automerge labels Aug 26, 2026
@vmartinv

Copy link
Copy Markdown
Collaborator

Thanks!

@kodiakhq
kodiakhq Bot merged commit cfd82a9 into cgt-calc:main Aug 26, 2026
14 checks passed
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Freetrade dates use the UTC calendar day instead of the UK one

2 participants