Skip to content

mattjgalloway/cgtcalc

Repository files navigation

cgtcalc: UK capital gains tax calculator

Build status codecov

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 full range of matches of acquisitions to disposals for the current capital gains tax system, namely supporting matching based on the following 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.

The calculator specifically only deals with shares where the acquisition 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 the acquisition proceeds. And it also supports handling of dividends within accumulation share classes of funds where those amounts can be subtracted from the disposal proceeds.

What does it not support?

Currently there is no support for:

  1. Transactions before 6th April 2008.
  2. Anything I haven't thought of (as I say in the disclaimer - I am not a financial professional).

Platforms

The library and console app that makes up cgtcalc both work fully on both macOS and Linux. It's running on a Linux server at https://cgtcalc.galloway.me.uk/.

Note: However as of writing this, there's a very serious bug with Decimal in Swift on Linux that means you should not use it there as you can easily end up in a situation where the calculations are wildly incorrect. The Swift bug can be tracked here: https://bugs.swift.org/browse/SR-13015

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. You can see more details about how it's being calculated if you pass the -v flag.

Full usage can be found by running with -h:

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

ARGUMENTS:
  <filename>              The input data filename

OPTIONS:
  -v, --verbose           Enable verbose logging
  -o, --output-file <output-file>
                          Output file
  --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>
CAPRETURN Asset event Capital return event (usually for a fund on first dividend distribution after purchase) <DATE> <ASSET> <AMOUNT> <VALUE>
DIVIDEND Asset event Dividend for which income tax has been taken but shares also retain (usually for fund accumulation share class) <DATE> <ASSET> <AMOUNT> <VALUE>
SPLIT Asset event Stock split <DATE> <ASSET> <MULTIPLIER>
UNSPLIT Asset event Stock un-split <DATE> <ASSET> <MULTIPLIER>

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    Exemption   Loss carry   Taxable gain   Tax (basic)   Tax (higher)
======================================================================================
2019/2020   £1140   £12000      £0           £0             £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 income portion of dividends (for accumulation fund share classes).

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

Unclear rules of semi-disposals

One complication with handling these is what to do when there are semi-disposals (i.e. not the full amount held at time of sale). It's unclear from documentation how that is handled. For example, consider the following set of transactions:

01/08/2019: BUY 10 at £100
01/09/2019: SELL 5 at £105
01/01/2020: BUY 10 at £90
01/04/2020: Dividend equalisation of £50 on 15 shares
01/04/2020: Dividend income of £30 on 15 shares

01/06/2020: BUY 10 at £80
01/07/2020: SELL 5 at £100
01/04/2021: Dividend equalisation of £10 on 10 shares
01/04/2021: Dividend income of £40 on 20 shares

It's unclear precisely which shares attract equalisation. In the case of the second dividend, depending on how things are calculated, there might be an equalisation payment or there might not be.

cgtcalc handles these cases by assuming FIFO for these purposes. So in the example above, the first dividend (both the equalisation and income) would be split 5/15 on the first buy, and 10/15 on the second buy. The second dividend's equalisation portion would be applied to the third buy alone. The second dividend's income portion would be split across the second and third buys at 10/20 each. The first buy doesn't attract the second dividend's income portion because that lot is fully sold by the FIFO ordering scheme.

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.

You can extend CGTCalcCore in two interesting ways:

  1. Create a custom parser to parse from your own format into what CGTCalcCore requires. The default parser that comes with the library is called DefaultParser.
  2. Create a custom presenter to process the output from CGTCalcCore and display it how you wish. The presenter that outputs in text format is called TextPresenter.

It's best to look at main.swift to see how to use CGTCalcCore. It's essentially as follows:

import CGTCalcCore
import Foundation

...

// Custom parser could be used here
let parser = DefaultParser()
let data = "... read from file ..."
let input = try parser.calculatorInput(fromData: data)

// Create calculator, feeding it the input created above, and then process it
let calculator = try Calculator(input: input, logger: logger)
let result = try calculator.process()

// Custom presenter could be used here
let presenter = TextPresenter(result: result)
let output = try presenter.process()

Tests

cgtcalc includes a comprehensive test suite. There are tests that cover the basic functionality through unit tests. There are also tests that take sample input and assert that the output matches that which is expected.

Input/output tests

The tests that take sample input and assert on the required output are the most interesting ones because you can see what the output of the tool is for a given input. These are full end-to-end tests.

The test that controls these tests can be found here: Tests/CGTCalcCoreTests/ExamplesTests.swift

First the test looks for all the input data files. Then it iterates over each of them and runs cgtcalc on the file. It finds the corresponding file in the output files and checks that the output is identical. Any difference is reported and the test failed.

It is also possible to have private tests which according to .gitignore will not be added to the repo. These live in Tests/CGTCalcCoreTests/PrivateExamples/. They can be used to have additional tests just on your local checkout. You might want to use this to put your inputs/outputs used for Self Assessment. Then each year when you update the software, you can check that nothing has changed.

Finally, if you want to re-record the tests, then you can set record to true when calling runTests in testExamples and testPrivateExamples. Note that if the output file doesn't exist then the output is recorded even if record mode is off.

Donate

If you like this and you'd like to buy me a coffee or a beer then I would say thank you and ask you to send to my PayPal.