Home Docs CSV import
Guides

CSV import

Bring your existing history into Capitrack by importing a CSV. The importer auto-detects exports from Revolut (stocks and commodities) and Trezor, and accepts a simple generic format for everything else. Duplicate rows are skipped automatically by fingerprint, so you can re-import the same file safely.

How importing works

Open an account and click Import in its toolbar to bring up the import modal. Capitrack reads the column headers, figures out which of the four formats it is, parses each row into a transaction, and adds it to that account. You can also import via the API.

  1. Pick or create an account

    Imported transactions are attached to one account, so open the right destination (e.g. “Stock Portfolio”) first.

  2. Choose your CSV

    The detected format is shown as a badge in the modal. If it reads Unknown, switch your file to the generic format below — nothing is imported for an unknown format.

  3. Review the result

    Capitrack reports how many rows were imported versus skipped as duplicates, along with the total parsed and the detected format.

Supported formats

Format detection is based on the headers present in your file (header matching is case-insensitive):

FormatDetected when headers includeCovers
revolut-stocksTicker and Price per shareBuys, sells, dividends, stock splits
revolut-commoditiesProduct, Started Date and StateGold, silver, platinum, palladium
trezorTransaction ID and Amount unitBTC, ETH, LTC and other crypto transfers
genericsymbol and typeAny source you can map yourself
Revolut commodity and Trezor crypto tickers are mapped to Yahoo Finance symbols automatically — for example gold becomes GC=F and Bitcoin becomes BTC-USD — so prices resolve correctly. The generic format performs no remapping; use Yahoo symbols directly.

Auto-detection & forcing a format

The modal first calls POST /api/transactions/import/detect to inspect the headers and pick a format. On import (POST /api/transactions/import/csv) the same detection runs again unless you pass an explicit format (revolut-stocks, revolut-commodities, trezor or generic). Forcing a format is handy when a generic file's headers happen to look like another format. CSV parsing trims fields, ignores blank lines, and tolerates ragged rows.

The generic format

The generic importer is the universal escape hatch and is the closest match to Capitrack's own Export CSV. Build a CSV with these columns (each header is accepted in lower-, Title- or UPPER-case):

ColumnRequiredNotes
symbolYesTicker, upper-cased on import, e.g. AAPL, BTC-USD
typeYesOne of the transaction types below (lower-cased)
dateYesUsed as-is, e.g. 2024-01-15
quantityNoNumber of units (defaults to 0)
priceNoPrice per unit (defaults to 0)
feeNoTransaction fee (defaults to 0)
currencyNoDefaults to EUR
notesNoFree-text label

The type column must be one of:

buy · sell · transfer_in · transfer_out · dividend · interest · fee

Example

my-portfolio.csvcsv
symbol,type,quantity,price,fee,currency,date,notes
AAPL,buy,10,150,1.00,USD,2024-01-15,Initial position
AAPL,sell,5,160,1.00,USD,2024-02-20,Trim
BTC-USD,transfer_in,0.05,42000,0,USD,2024-01-10,From cold wallet
VWRL,dividend,0,0,0,EUR,2024-03-01,Q1 dividend
Rows missing a symbol or date, or with a type outside the list above, are silently skipped — check those columns first if rows don't appear.

Broker formats & mapping

Each broker parser remaps source tickers and rows onto Capitrack's transaction types. A recurring rule: a dividend (or a cash-style commodity exchange) is recorded as an amount — the cash value goes into quantity and price is set so the total reflects it (price 1 for dividends, 0 for commodity exchanges).

Revolut — Stocks

Reads Type, Ticker, Quantity, Price per share, Total Amount, Currency and Date. Monetary fields are stripped of currency symbols and separators; the date is normalised to YYYY-MM-DD.

Source TypeMapped typeQuantityPrice
BUY - MARKETbuyQuantityPrice per share
SELL - MARKETsellQuantityPrice per share
DIVIDENDdividendTotal Amount1
STOCK SPLITtransfer_inQuantityPrice per share (or 0)
CASH TOP-UP / CASH WITHDRAWAL / blank tickerskipped

Revolut — Commodities

Only rows with State == COMPLETED are processed. The metal code in the Currency column is mapped to a Yahoo futures symbol — XAU → GC=F, XAG → SI=F, XPT → PL=F, XPD → PA=F — and direction is inferred from the Description:

  • contains Exchanged to EUR or Exchanged to USDsell (metal sold for fiat)
  • otherwise starts with Exchanged tobuy (fiat exchanged into metal)
  • anything else → skipped

quantity is the Amount, price is 0 (an amount-style record), fee is the Fee, and the currency is recorded as EUR. The date comes from Started Date (falling back to Completed Date).

Trezor

Reads Type, Amount, Amount unit, Fiat (USD), Fee and Date. The unit is mapped to a Yahoo crypto symbol — BTC → BTC-USD, ETH → ETH-USD, LTC → LTC-USD; any other unit becomes <unit>-USD.

Source TypeMapped type
RECVtransfer_in
SENTtransfer_out
otherskipped

quantity is the Amount; price is a derived per-unit USD price (Fiat (USD) / Amount) so quantity × price ≈ the USD value of the transfer; the currency is USD. The M/D/YYYY date is normalised to YYYY-MM-DD; rows with no date or a zero amount are skipped.

Deduplication

Every parsed row is reduced to a fingerprint and compared against the transactions already in the target account (and against rows seen earlier in the same file). Matching rows are skipped, not inserted. The fingerprint is:

fingerprinttext
{account_id}|{symbol}|{type}|{quantity:F8}|{price:F4}|{date}

Quantities are compared to 8 decimals, prices to 4, and only the calendar date matters (anything after a T or space is dropped). That means you can safely:

  • Re-import an updated export without creating duplicates.
  • Import overlapping date ranges from the same broker.
  • Run the same file twice if an import was interrupted.

The import result

Both the UI and the API return the same counts. total is the number of parsed (mappable) rows; rows a parser skips entirely (wrong state, unsupported type, missing date) never reach this count:

200 OKjson
{
  "imported": 8,
  "skipped": 2,
  "total": 10,
  "errors": [],
  "format": "revolut-stocks"
}

Staging files for import

The api container mounts a host folder read-only at /app/transactions (the repo's ./transactions directory). It's a convenient place to keep CSV files, but Capitrack does not auto-import from it — you always import explicitly through an account's Import button or the API.