SkillAgentSearch skills...

Cgtcalc

A UK capital gains tax calculator written in Swift

Install / Use

/learn @mattjgalloway/Cgtcalc
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

cgtcalc: UK capital gains tax calculator

Build status codecov

Note: This project was fully rewritten (AI natively) for the 0.2.0 release.

DISCLAIMER: I am not a financial professional and cannot give tax advice. This calculator is intended for sample purposes only. Always do your own calculations for your self assessment. I accept no responsibility for any errors this calculator produces.

cgtcalc is a command line application written in Swift which calculates UK capital gains tax based on a set of transactions. You feed it an input list of transactions and it outputs a summary of each tax year's gain (or loss) and the details for the disposal events that make up the gain (or loss).

Why?

I developed this because I needed something that would take my transactions and calculate capitals gains tax. I wanted something which would check my manual working. I developed it in Swift because I wanted to see how building a console app in Swift was.

There are other excellent calculators out there, such as CGTCalculator, however I couldn't find one which did everything that I need. The missing piece seemed to be handling of fund equalisation payments and dividends that need to be accounted for in accumulation funds.

What does it support?

Currently the calculator supports the following acquisition-to-disposal matching rules, in this order:

  1. Same day trades.
  2. Bed & breakfast trades where you purchase an asset within 30 days of selling the same asset.
  3. Section 104 holding.

Important assumption:

  • 30-day matching currently assumes the disposer is UK resident at the time of the later acquisition.

The calculator specifically only deals with shares where acquisitions and disposals are all on or after 6th April 2008 when the latest rules came into effect.

It also supports handling of equalisation payments (capital return) for funds where those amounts should be subtracted from allowable cost, and accumulation distributions (dividend rows) where those amounts should increase allowable cost.

What does it not support?

Currently there is no support for:

  1. Transactions before 6th April 2008.
  2. The additional HMRC identification fallback where, if same-day + 30-day + Section 104 still do not fully identify a disposal, the remainder is matched with later acquisitions beyond the 30-day window.
  3. Anything not represented by the supported input row types documented below.

Unsupported Identification Cases

If an input requires share identification beyond:

  1. same-day acquisitions
  2. acquisitions in the following 30 days
  3. Section 104 pooling

the calculator currently does not implement HMRC's further "later acquisitions" fallback stage. In those cases the engine will fail with an insufficientShares calculation error. the calculator currently does not implement HMRC's further "later acquisitions" fallback stage. In those cases the engine raises an unsupportedLaterAcquisitionIdentification calculation error.

Platforms

The library and console app both run on macOS and Linux. Text output works on both platforms. PDF output is currently available on macOS only.

Usage

Using cgtcalc is simple. All you need to do is the following:

  1. Clone the repository.
  2. Put your transactions into a file, such as data.txt.
  3. Run swift run cgtcalc data.txt.

That's pretty much it. You'll then see output on your console showing the calculations and a summary for all tax years that have tax events in them. By default the output format is text. You can select another formatter using --format. For PDF output, use --format pdf --output-file report.pdf (available on macOS only).

Full usage can be found by running with -h:

USAGE: cgtcalc <filename> [--output-file <output-file>] [--format <format>]

ARGUMENTS:
  <filename>              The input data filename (use '-' for stdin)

OPTIONS:
  -o, --output-file <output-file>
                          Output file
  -f, --format <format>   Output format (text or pdf on macOS, text only on Linux)
  --version               Show the version.
  -h, --help              Show help information.

Input data

Each row of the input file starts with the kind of data followed by details. For example a buy transaction, for 200 shares of LON:FOOBAR on 01/01/2020 at £1.50 with £20 expenses would be as follows:

BUY 01/01/2020 LON:FOOBAR 200 1.5 20

The full list of kinds of data are as follows:

| Kind | Category | Description | Fields | |-------------|--------------|-----------------|------------| | BUY | Transaction | Buy transaction | <DATE> <ASSET> <AMOUNT> <PRICE> <EXPENSES> | | SELL | Transaction | Sell transaction | <DATE> <ASSET> <AMOUNT> <PRICE> <EXPENSES> | | SPOUSEIN | Transaction | No-gain/no-loss transfer in from spouse/civil partner (manual cost basis input) | <DATE> <ASSET> <AMOUNT> <PRICE> | | SPOUSEOUT | Transaction | No-gain/no-loss transfer out to spouse/civil partner (costed using normal share-identification ordering) | <DATE> <ASSET> <AMOUNT> | | CAPRETURN | Asset event | Capital return / equalisation event which reduces allowable cost | <DATE> <ASSET> <AMOUNT> <VALUE> | | DIVIDEND | Asset event | Accumulation distribution which increases allowable cost | <DATE> <ASSET> <AMOUNT> <VALUE> | | SPLIT | Asset event | Stock split | <DATE> <ASSET> <MULTIPLIER> | | UNSPLIT | Asset event | Stock un-split | <DATE> <ASSET> <MULTIPLIER> | | RESTRUCT | Asset event | Exact-ratio share restructure using old:new units | <DATE> <ASSET> <OLD>:<NEW> |

Rows must contain exactly the fields shown above; extra trailing fields are rejected.

Notes for spouse transfers:

  • SPOUSEOUT does not take price or expenses. The calculator derives transferred allowable cost using normal share-identification ordering (same day, then following 30 days, then Section 104 fallback) and reports it in a dedicated SPOUSE TRANSFERS OUT section.
  • SPOUSEIN should use the per-unit cost basis manually transcribed from the transfer-out calculation run for the other person.
  • This tool calculates one person at a time; use separate input files/runs for each spouse/civil partner.

Example

Given the following input in a file called data.txt:

BUY 05/12/2019 GB00B41YBW71 500 4.7012 2
SELL 28/11/2019 GB00B41YBW71 2000 4.6702 12.5
BUY 28/08/2018 GB00B41YBW71 1000 4.1565 12.5
BUY 01/03/2018 GB00B41YBW71 1000 3.6093 2

The tool can be invoked like so:

swift run cgtcalc data.txt

And will output the following:

# SUMMARY

Tax year    Gain    Proceeds   Exemption   Loss carry   Taxable gain
====================================================================
2019/2020   £1140   £9340      £12000      £0           £0
# TAX YEAR DETAILS

## TAX YEAR 2019/2020

1) SOLD 2000 of GB00B41YBW71 on 28/11/2019 for GAIN of £1140
Matches with:
  - BED & BREAKFAST: 500 bought on 05/12/2019 at £4.7012
  - SECTION 104: 2000 at cost basis of £3.89015
Calculation: (2000 * 4.6702 - 12.5) - ( (500 * 4.7012 + 2) + (1500 * 3.89015) ) = 1140


# TRANSACTIONS

05/12/2019 BOUGHT 500 of GB00B41YBW71 at £4.7012 with £2 expenses
28/11/2019 SOLD 2000 of GB00B41YBW71 at £4.6702 with £12.5 expenses
28/08/2018 BOUGHT 1000 of GB00B41YBW71 at £4.1565 with £12.5 expenses
01/03/2018 BOUGHT 1000 of GB00B41YBW71 at £3.6093 with £2 expenses


# ASSET EVENTS

NONE

Accounting for dividends

NOTE: The information in this section is my own interpretation. See disclaimer at the top of this README.

Dividends in funds need special care when accounting for CGT. In both income and accumulation funds, there is an equalisation part of the first dividend payment after shares in the fund are acquired. This part is classed as a return of capital on the initial investment. This therefore means the cost basis of the acquisition needs to be lowered. The remainder of that first dividend (and all of subsequent dividends) are treated as normal income. That normal income doesn't need to be accounted for in income fund classes, but does in accumulation funds because that income is re-invested and so does not need to attract CGT as it will have attracted income tax already.

cgtcalc can handle both the equalisation portion of dividends (for income and accumulation fund share classes) and the accumulation-distribution portion of dividends (for accumulation fund share classes).

The equalisation portion is a CAPRETURN asset event. The income portion is a DIVIDEND asset event.

Important semantics:

  • DIVIDEND means an accumulation distribution. It does not mean an ordinary cash dividend.
  • CAPRETURN and DIVIDEND should be dated using the effective / entitlement date for the holding, not merely the later cash reporting date.
  • Same-day DIVIDEND rows for one asset/date are validated by their summed amount, which must match the holding quantity on that date.
  • Same-day CAPRETURN rows for one asset/date are validated by their summed amount, which must match the Group II tranche for that distribution period, i.e. the units bought since the last distribution date and still held at the event date.
  • Asset-event amount validation allows a very small decimal tolerance to avoid rejecting mathematically equal split rows because of Decimal representation noise.
  • Same-day same-asset sells are merged into one effective disposal for calculation and rounding.

Extending

cgtcalc is broken into two parts:

  1. A library called CGTCalcCore which contains all of the calculation logic.
  2. A simple console app called cgtcalc which uses CGTCalcCore.

Tests

cgtcalc includes a layered test suite. There are focused unit tests for smaller calculator compo

View on GitHub
GitHub Stars120
CategoryDevelopment
Updated4d ago
Forks21

Languages

Swift

Security Score

95/100

Audited on Mar 22, 2026

No findings