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.
-
Pick or create an account
Imported transactions are attached to one account, so open the right destination (e.g. “Stock Portfolio”) first.
-
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.
-
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):
| Format | Detected when headers include | Covers |
|---|---|---|
revolut-stocks | Ticker and Price per share | Buys, sells, dividends, stock splits |
revolut-commodities | Product, Started Date and State | Gold, silver, platinum, palladium |
trezor | Transaction ID and Amount unit | BTC, ETH, LTC and other crypto transfers |
generic | symbol and type | Any source you can map yourself |
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):
| Column | Required | Notes |
|---|---|---|
symbol | Yes | Ticker, upper-cased on import, e.g. AAPL, BTC-USD |
type | Yes | One of the transaction types below (lower-cased) |
date | Yes | Used as-is, e.g. 2024-01-15 |
quantity | No | Number of units (defaults to 0) |
price | No | Price per unit (defaults to 0) |
fee | No | Transaction fee (defaults to 0) |
currency | No | Defaults to EUR |
notes | No | Free-text label |
The type column must be one of:
buy · sell · transfer_in · transfer_out · dividend · interest · fee
Example
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
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 Type | Mapped type | Quantity | Price |
|---|---|---|---|
BUY - MARKET | buy | Quantity | Price per share |
SELL - MARKET | sell | Quantity | Price per share |
DIVIDEND | dividend | Total Amount | 1 |
STOCK SPLIT | transfer_in | Quantity | Price per share (or 0) |
CASH TOP-UP / CASH WITHDRAWAL / blank ticker | skipped | ||
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 EURorExchanged to USD→sell(metal sold for fiat) - otherwise starts with
Exchanged to→buy(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 Type | Mapped type |
|---|---|
RECV | transfer_in |
SENT | transfer_out |
| other | skipped |
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:
{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:
{
"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.