Extra Data and Options¶
Most users can generate a report from a supported broker export without these files or options. Start with your broker guide. Return here only if cgt-calc asks for more information or one of the tax settings below applies to your investments.
Automatic data fetching¶
cgt-calc creates and updates these local files itself. You do not need to create or select them for a normal calculation.
Exchange rates¶
cgt-calc converts other currencies to pounds at HMRC's monthly exchange rates.
- April 2002 to April 2016: the rates come with cgt-calc. In those years HMRC also changed some rates during the month, and each change is used from the day it applied. For example, the US dollar rate for November 2008 was 1.6336 until the 18th and 1.5047 from the 19th.
- May 2016 onwards: cgt-calc downloads each month's rates, from
HMRC's legacy service up to 2020
and from the UK Trade Tariff API for
2021 onwards. It saves them to
out/exchange_rates.csv.
A row in that file for a date from April 2002 to April 2016 is compared with the rate that comes with cgt-calc. Where the two differ, cgt-calc prints a warning naming the row and HMRC's rate, on every run until you remove the row:
- Before February 2015 it uses your row. Nothing could be downloaded for those dates, so the row is one you typed in. Check it against HMRC's rate in the warning: a row typed from a month's table can miss a change HMRC made during the month.
- From February 2015 to April 2016 it uses HMRC's rate. A file written by an earlier version of cgt-calc can hold such rows, because it saved the rate from before HMRC's change.
A row you add for a date from May 2016 is used as it stands, without a comparison. When that date needs another currency, cgt-calc downloads the month and saves the other currencies beside your row. To go back to HMRC's rate, remove the row: on the next run cgt-calc downloads HMRC's rate and saves it in the row's place.
The rates are HMRC's as published, and cgt-calc cannot tell when HMRC's own figure is wrong. HMRC's tables give some less common currencies another currency's rate, for example the Georgian lari at the Russian rouble's rate until September 2013 and the Liberian dollar at the US dollar's. For a transaction in a currency like these before February 2015, compare HMRC's rate with another source and add your own row if it is wrong. From February 2015 to April 2016 HMRC's rate cannot be replaced.
cgt-calc stops and names the currency and date when it has no rate for a transaction:
- A date before April 2002. HMRC's archived rates start in April 2002.
- A currency HMRC did not list that month, or listed under another code. HMRC's tables give the Russian rouble as RUR, the Turkish lira as TRL and the Mexican peso as MXV until December 2009, and the Romanian leu as ROL until February 2009. A few rows have no code at all, such as the Romanian leu in January 2010.
- A currency and month for which HMRC's documents disagree or cannot be read, so the rate HMRC
applied cannot be told. There are 44, including the Mexican peso in December 2009 under both of
HMRC's codes (MXV, MXN). The others are minor currencies, mostly the UAE dirham (AED), the East
Caribbean dollar (XCD) and the CFA and CFP francs (XOF, XPF) in some months from 2006 to 2013.
Each year's file lists its own at the top, as in
2009.csv.
To continue, add a row to the file yourself: the transaction's date in the month column, the
currency code, and the rate as units of that currency per £1. Each date and currency needs one row.
Offshore yuan (CNH) is converted at the CNY rate, so enter it as CNY. If the file does not exist
yet, start it with the header line month,currency,rate.
For a date from May 2016, cgt-calc also stops when HMRC's rates cannot be downloaded, and says why. Try again later first. Adding the rate yourself works too: read it from the HMRC pages linked under “May 2016 onwards” above. Each run stops at the first date and currency without a row, though, so a history with many dates needs as many rows before it completes.
HMRC does not prescribe which exchange rate to use, only that the method is reasonable and consistent (CG78310). HMRC's own figure for the date keeps the transaction in line with the rest of the report:
- For a currency listed under an older code or without one, use HMRC's rate in that row. HMRC's TRL rates until 18 January 2005 are in old lira, a million of which make one new lira (TRY), and its ROL rates until 12 July 2005 are in old lei, 10,000 of which make one new leu (RON).
- Where HMRC's documents disagree, look up the month in the archive below, use the figure you can best justify, and note why.
- Before April 2002, the Bank of England publishes daily spot exchange rates against sterling.
HMRC's rates up to 2014 are kept in the UK Government Web Archive:
- April 2002 to 2007: open
HMRC's exchange rates for 2007
and change
EXRATES_2007in the address to the year you need. Until August 2006 each month's table is a PDF or Word download from its page. - 2008 to 2014: open
HMRC's exchange rates for 2009
and choose “Rates of Exchange for Customs and VAT purposes” for the month. For another year,
change
EXRATES_2009in the address to that year.
A month's table gives a “Date of change” and “New rate” where HMRC changed a rate during the month, and its “Weekly Rates of Exchange amendments” list every change.
Use --exchange-rates-file to select another file in the same format. An empty value disables the
cache. Keep the completed file with the report to preserve the exchange rates used. cgt-calc may add
missing months to the selected file, so retain the version used for the final report. Rates that
come with cgt-calc are not written to the file. Keeping it does not preserve other fetched data,
such as Yahoo Finance prices.
ISIN to ticker translation¶
When an ERI row identifies a fund only by its ISIN, cgt-calc maps it to ticker symbols. It checks
existing mappings first and queries the Open FIGI API only
if needed. It saves new mappings in out/isin_translation.csv by default; --isin-translation-file
selects another path.
To add or correct a mapping yourself, edit that file, or create it if it does not exist yet. It is a
CSV file that starts with the header line ISIN,symbol. Each row after it gives one ISIN, then
every ticker that security is known by, each in its own column. For a fund listed under both VAPX
and VDPX:
ISIN,symbol
IE00B9F5YL18,VAPX,VDPX
That row is already in the bundled list and only shows the format. When you write your own:
- Write each ticker exactly as your broker file gives it, or the current ticker where cgt-calc
reads an old ticker as the current one, such as
METAforFB. The report may show a supported pair under one ticker, which is not always the one in your file. - A Vanguard holding with no ticker goes under its fund name, on the row of its own ISIN. The no ISIN mapping was found warning lists the names as they must be written, each in double quotes where it contains a comma.
- Give each ISIN one row. A row for an existing ISIN replaces its bundled symbols, so put every verified ticker for that ISIN on the same row. If two rows give the same ISIN different tickers, cgt-calc stops and names both rows.
- Give each ticker to one ISIN only. A ticker under two stops the run; see
already linked to ISIN.
Two tickers on one row are not combined into one holding
cgt-calc combines only the ticker pairs it already supports, and any other ticker keeps a holding of its own. The row also stops cgt-calc refusing transactions that give that ISIN both tickers, so check the report after adding it. If the report shows the security twice, do not rely on those holdings' calculated gains. Open a GitHub issue with the ISIN and both tickers so that the pair can be added.
cgt-calc can create or rewrite the file after a successful lookup. The file holds only the mappings
you add and the ones a lookup finds. cgt-calc reads them on top of the bundled
initial_isin_translation.csv,
which is never copied into the file. Tickers read from your broker transactions are used for the run
but never written to the file. A file written by an earlier version of cgt-calc can hold them; they
are read like any other row, so remove one that is wrong.
already linked to ISIN¶
Two rows, or a row and the bundled list, give the same ticker to different ISINs. The error names
your row and says where the other ISIN is: on another row, or in the bundled list. Correct the row
that is wrong. If the ticker is right for your ISIN and the clash is with the bundled list, also
give the bundled ISIN a row that leaves that ticker out. If that was its only ticker, write the ISIN
followed by a comma, such as IE00B42WWV65,.
If the error names no row, a lookup found a ticker that another ISIN already has. Add a row for the ISIN that was looked up, which is the second one in the error, with its correct ticker. If that ticker is the one in the error, also take it off the first ISIN: change its row if your file has one, or give it a row that leaves the ticker out.
When extra information is needed¶
Missing share prices¶
cgt-calc sometimes needs a share's price on a date that your broker files do not give:
- A vest without a price. A vest or other stock-plan acquisition needs a price per share to
establish its cost. Broker files usually provide it, and cgt-calc includes some historical USD
values in
share_prices.csv. If cgt-calc stops with a No share price error, give the missing price in the currency of the transaction it is for. - A spin-off. cgt-calc splits the old holding's cost between the old and the new holding by their market values on the date of the spin-off row, using each holding's closing price that day from Yahoo Finance, as traded rather than adjusted for later dividends and splits. If it stops with a No market data found error, for example because Yahoo lists the ticker differently from your broker, give the closing prices of both holdings on that date as they traded then, not the split-adjusted figures on Yahoo's history page. The file has no currency column, so write both prices in the same currency; for London shares, do not mix pence and pounds. Prices you give are used instead of Yahoo's, and giving only one of the two stops the run.
Put the prices in a CSV file in the same format as share_prices.csv, under the tickers in your
broker files, not Yahoo's, and pass it with --prices-file. The first line must be the header
date,symbol,price. For a spin-off of NEWCO from ACME on 1 April 2024:
date,symbol,price
"Apr 01, 2024",ACME,94.00
"Apr 01, 2024",NEWCO,17.50
Your file replaces the bundled prices rather than adding to them, so copy in any bundled rows you
still need. A row under a ticker the company has since changed, such as FB, counts for its current
ticker, META, over the dates given under Ticker renames: an FB row dated from
26 June 2025 prices the security that has traded as FB since then, not Meta. If two rows give the
same share different prices on one day, cgt-calc stops and asks you to remove one.
Spin-off source mappings¶
A spin-off row may name the new holding without naming the old holding it came from. cgt-calc needs
that link to divide the existing pooled cost between them. During an interactive run, it asks for
the old ticker and saves the answer to out/spin_offs.csv.
If the run cannot ask you for the old ticker, put the mapping in a file, pass that file with
--spin-offs-file, and rerun. The file starts with the header line dst,src. Each row after it
gives the new ticker, then the ticker it was spun off from. For NEWCO spun off from ACME:
dst,src
NEWCO,ACME
If two rows give the same new ticker different old tickers, cgt-calc stops and names both rows.
An empty --spin-offs-file value disables the cache, so cgt-calc does not read or save spin-off
mappings.
Ticker renames¶
When a company changes its ticker, your history can show the same shares under both: bought as FB,
for example, and sold as META. cgt-calc reads a known old ticker as the current one, so
transactions under both names are one holding, shown under the current ticker in the report. The
renames it knows are listed in
ticker_renames.csv,
with each company's ISIN, the 12-character code that identifies a security.
Another security can take a ticker after a company gives it up, so cgt-calc checks that a row is the renamed company's:
- A row with an ISIN is renamed when the ISIN is the company's, whatever the row's date.
- A row without an ISIN, such as a Schwab or RAW row, is renamed from the table's
sincedate up to the day before itsreuseddate, when another security started trading under the old ticker. A blank column sets no limit on that side. Theuntilcolumn, the day the company changed ticker, does not limit it, because a broker can keep the old ticker on later rows.FBrows are renamed from 18 May 2012 to 25 June 2025, so anFBrow from 26 June 2025 is left asFB.
If one file has rows without an ISIN under an old ticker, some inside its renamed period and some
outside it (for FB, before 18 May 2012 or from 26 June 2025), cgt-calc stops with
Rows under <ticker> fall on both sides of <date>: nothing in the rows says whether they belong to
one company or two. In a RAW file, write the current ticker on every row of the renamed company, as
the message says. cgt-calc checks each file on its own, so rows in separate files are each read by
their own date without an error.
Two limitations remain:
- A row without an ISIN is matched by its ticker and date alone. The table includes a rename only when no other security is known to have used the old ticker in that period on an exchange a supported broker offers. If you held another security under the old ticker in that period, cgt-calc cannot detect it: it is reported under the new ticker, and pooled with the company's shares if you also held them. Check each holding and disposal in the report against your records.
- A rename missing from the table leaves the old and new tickers as two holdings, except in a
Vanguard export, whose
NameChangerows record the change, and in Trading 212, which stops on a ticker change it cannot pair. cgt-calc may stop withTried to sell, or withdoes not matchwhen rows give one ISIN under both tickers, but a sale can also take its cost from the new ticker's purchases alone without an error. Check the portfolio section of the report: a holding left under a ticker the company no longer uses can mean a rename is missing.
If a rename is missing or wrong, or a broker export stops with the Rows under <ticker> message,
first upgrade cgt-calc using the same method you used to install it. If the problem remains, open a
GitHub issue with the old and new
tickers, the ISIN and the dates involved. Do not edit a broker export to get past it. In a RAW file
you write yourself, you can use the current ticker throughout instead.
Estimate unrealized gains¶
Use --unrealized-gains to add current-price estimates to the portfolio and summary printed in the
terminal. For each holding left at the report's end date, cgt-calc uses the latest usable price
returned by Yahoo Finance, converts it to GBP and subtracts its Section 104 pooled cost. If no
usable price is returned, the holding is shown as unknown, omitted from the numeric Unrealized
gains total and covered by a warning. Check unknown or unexpected estimates against your broker.
This is a portfolio estimate, not the gain from an actual sale. It does not include selling costs or change the report's capital gains, losses or taxable figures. For an older report, the ending portfolio may not be what you hold today. See Privacy and data security for details of the Yahoo lookup.
Bond-fund income¶
Some offshore bond funds have income that must be reported as interest rather than dividends. After
checking the fund's classification, pass its ticker to --interest-fund-tickers. Use a
comma-separated list for several tickers. This setting applies to both cash distributions and ERI;
see the offshore-fund checklist. It reports the income as foreign
interest, so do not use it for a UK fund. If cgt-calc reports a UK bond fund distribution as a
dividend, adjust the income figures outside cgt-calc.
Income reported in more than one currency¶
Two brokers reporting the same dividend in different currencies, or a dividend and its withholding tax in different currencies, stop the calculation with Cannot combine amounts in different currencies or Dividend and withholding tax currencies do not match.
If the rows really are the same dividend and use GBP plus one foreign currency, pass
--autoconvert-currency:
cgt-calc --year 2024 --revolut-file revolut.csv --trading212-dir trading212/ --autoconvert-currency
cgt-calc then reports them as one figure, converting the foreign amounts at the HMRC rate for the date of the dividend. Withholding posted in a later month is converted at that same rate, not the rate for the month it posted in. Amounts already in GBP are used as they are.
The option is off by default, and it cannot tell whether two rows with the same ticker and date describe the same security and payment. Compare the ISIN and payment details on each broker's statement before enabling it. Two different non-GBP currencies remain an error: the option does not convert between foreign currencies. If your broker cannot provide a verified amount in one currency, report that dividend outside cgt-calc.
Where an ISIN is available, cgt-calc uses it to determine the dividend's source country. Without an ISIN, it falls back to the currency of the dividend itself, never that of the tax. For a dividend your broker reports already converted to GBP, cgt-calc therefore cannot determine a treaty automatically: it warns that the source country is unknown and leaves the relief out of the report.
A payment currency does not prove where the paying company is resident, especially when a broker converts payments into your account currency. HMRC's dividend treaty guidance bases treaty treatment on the residence of the company paying the dividend. Check that residence and the tax deducted on your broker's tax voucher. If the fallback is wrong or missing, add a verified mapping under ISIN to ticker translation; if you cannot, calculate the relief outside cgt-calc rather than relying on the treaty figure in the report.
An ISIN does not always show where the company is resident either. cgt-calc reads the country from
its first two letters, and a company based elsewhere can have an ISIN that starts US, as with the
New York shares of a Dutch company or a depositary receipt. Where the tax on its dividend was
withheld at 15%, which is also the US rate, the report applies the US treaty and shows 15% relief
without a warning. The treaty with the company's own country may allow less, such as 10% for the
Netherlands (DT14005).
Check the treaty figure for any share whose company is not based in the country its ISIN starts
with, and calculate the relief outside cgt-calc if it is wrong.
Shares held before April 2008¶
Since 6 April 2008, all your shares in one company are pooled at what they cost, whenever you bought them (CG51550). cgt-calc applies these rules to your whole history, so purchases from before 2008 count towards the cost of shares you sell now. Two kinds of earlier history are refused, with an error naming the transaction:
- A sale, gift or other disposal before 6 April 2008. Earlier disposals followed different
rules, which decided which shares you still held, and cgt-calc does not implement them. Replace
that holding's rows before 6 April 2008 with one
BUYin a RAW file dated 5 April 2008, for the shares you still held and their total cost from your records of the time. Enter the cost in sterling, with the currencyGBP: a cost in another currency would be converted at the 5 April 2008 rate, but each purchase's cost converts at the rate on the day it was made (CG78310). Use the cost without indexation: an old pool statement may show an indexed figure beside it. ABUYwith no deposit to pay for it fails the cash balance check, so add aTRANSFERrow for the same amount on the same date, or run with--no-balance-check. - Anything before 6 April 1982. Shares held on that date are pooled at their 31 March 1982 market value, which cgt-calc cannot know.
A transaction in another currency before April 2002 also needs its rate added by hand; see Exchange rates.
CGT-exempt instruments (advanced)¶
Most users should not use --cgt-exempt-tickers. Pass a comma-separated list only when you have
established that each instrument is exempt under
TCGA 1992 s115.
- This is your own unverified assertion. The calculator does not identify or validate gilts or qualifying corporate bonds (QCBs), and QCB status cannot be inferred from a ticker or name.
- The override disregards both gains and losses on disposals. Do not use it where a disposal can still produce a charge: disposing of a QCB acquired on a takeover or reorganisation can bring a deferred gain back into charge, and the profit on a deeply discounted security is taxable as income. Neither is calculated here.
- The per-disposal breakdown in the PDF applies the ordinary same-day, 30-day and Section 104 share identification rules. Those are not the identification rules HMRC gives for gilts, QCBs and other relevant securities, so read the breakdown as workings for the quantities only. It has no effect on the figures reported, because the gain or loss is disregarded.
- Coupon and accrued interest remain taxable income. The calculator does not implement the Accrued Income Scheme.
- The importers for Freetrade, Hargreaves Lansdown and Interactive Brokers are documented as unvalidated for bonds and gilts. This override does not validate them either.
- The override changes the capital gains calculation only. A coupon is reported in whichever box its
broker rows put it in.
--interest-fund-tickersreports as foreign interest, so it is not a fix for a UK gilt coupon. Check the interest and dividend figures against your own records. - Matching uses the ticker at the disposal, so an instrument renamed part-way through the history has to be listed under both names.
- A gift of a listed instrument is reported as an exempt disposal and does not also appear in the Gifts at market value section.
- Listing an instrument does not lift the restrictions on disposing of it in more than one way on a single day. A sale and a gift to a connected person on the same day are still refused, even though the loss they cannot apportion would be disregarded.