FeesTERComparison

TER, ongoing charges and fund fees: how to compare fund costs programmatically

TER vs OCF vs total cost of ownership, why 0.20 % and 1.50 % end up 30 % apart over 25 years, and how to parse and rank fund fees from API data.Published 5 August 2026 · 2 min read · by FundFacts API

Fees are the one input to fund returns that is known in advance, which makes them the most useful field for screening. This article untangles the acronyms, shows the long-term arithmetic and gives you code to parse, normalise and rank fund costs from the headlineMetrics.ter field of a fund data API.

The acronyms

TermWhere you see itWhat it includes
TER – Total Expense RatioFactsheets, ETF product pagesManagement fee + administration, custody, audit, legal, regulatory costs, as a % of average assets
OCF / Ongoing chargesUCITS KIID, UK documentsEssentially the same as TER; the regulatory name
Management fee / AMCProspectusOnly the manager's fee — a subset of TER
Transaction costsPRIIPs KIDTrading costs inside the fund, disclosed separately
Performance feeProspectus, KIDCharged only above a hurdle; excluded from TER
Entry / exit chargesKID, platformOne-off, often waived by platforms

Rule of thumb: TER ≈ OCF ≈ "the running cost". It is what headlineMetrics.ter contains, as published: "0.20%".

Why small differences matter

Fees compound in reverse. On a €100,000 investment growing at 6 % gross:

TERValue after 25 yearsFees paid (incl. lost growth)
0.10 %€419,000€10,000
0.20 %€409,000€20,000
0.75 %€358,000€71,000
1.50 %€300,000€129,000

A 1.3-percentage-point fee gap becomes a 27 % difference in final wealth. This is why fee ranking is the first filter in most fund screeners.

typescript
export function finalValue(principal: number, grossReturn: number, ter: number, years: number) {
return principal * Math.pow(1 + grossReturn - ter, years);
}
finalValue(100_000, 0.06, 0.0020, 25); // ≈ 409,000

Parsing the fee from API data

Formatted metrics are returned as strings so the unit is never lost. Convert once at the edge:

typescript
export function pct(s: string | null | undefined): number | null {
if (!s) return null;
const m = /-?\d+(\.\d+)?/.exec(s.replace(",", "."));
return m ? Number(m[0]) : null;
}
const ter = pct(fund.data.headlineMetrics.ter); // "0.20%" -> 0.2

Then ranking a universe is a sort:

typescript
const ranked = funds
.map((f) => ({ isin: f.isin, name: f.name, ter: pct(f.data.headlineMetrics.ter) }))
.filter((f) => f.ter != null)
.sort((a, b) => a.ter! - b.ter!);

Fee is not the whole cost: tracking difference

For index funds the fee you *pay* is the tracking difference — the gap between the fund's return and the index's. Securities-lending income can offset part of the TER; sampling and cash drag can add to it. Compare both:

typescript
const oneYear = fund.data.annualisedReturns.find((r) => r.label === "1 Year");
const trackingDifference = oneYear && oneYear.index != null ? oneYear.fund! - oneYear.index : null;
// e.g. TER 0.20 %, tracking difference +0.13 % -> lending income more than covered the fee

Comparing share classes

The same fund often has a cheap institutional class and an expensive retail class with different ISINs. When you build a comparison, group by fund name and show the cheapest accessible class — the keyFacts.manager, benchmarkName and investmentObjective fields are identical across classes, which makes grouping straightforward.

What to display

  1. TER as published (headlineMetrics.ter).
  2. Cost in currency per year on the client's amount — €20 per €10,000 lands better than 0.20 %.
  3. Tracking difference for index products.
  4. A note on other costs (platform fee, transaction costs, FX) so the TER is not mistaken for the total cost of ownership.

Try it on your funds

Every response from FundFacts API includes headlineMetrics.ter alongside the performance series, so a fee-versus-return scatter plot for a whole universe is one loop. Create a free key or see the field reference.

Try it on your own ISINs

One request returns key facts, holdings, risk and performance as JSON. Free plan, no card.