Trades CSV format
The import file is comma-delimited CSV with RFC 4180 quoting. Uploaded files are decoded for you — UTF-8, UTF-16, or cp1252, with or without a byte-order mark — so a spreadsheet's "Unicode Text" save works as well as a plain one. Export and re-import round-trip losslessly.
If you just want to get a file in, start with Trades and CSV import. This page is the reference you come back to when a row won't take.
This is the format for holdings-tracked accounts. Balance-tracked accounts have their own, much shorter one — Balances CSV format.
The header row
The first line must be exactly this, all fifteen columns, in this order:
date,account,symbol,type,side,quantity,price,fees,amount,note,underlying,expiry,strike,right,seq
Nothing else is accepted — not a subset, not a reordering, not your broker's own header. If you'd rather not type it, use Download template in the import dialog: it gives you the header plus two example rows.
Columns
| Column | Required | What it holds |
|---|---|---|
date |
yes | YYYY-MM-DD. |
account |
yes | Account name, matched case-insensitively against your non-archived accounts. An unmatched name is a row error — import never creates accounts. |
symbol |
depends | Ticker or symbol, as-is. Leave it blank for a pure cash row — allowed only when side is transfer, interest, or fee. Required for buy, sell, dividend, and split. |
type |
no | equity, etf, fund, crypto, or option. Blank means equity. |
side |
yes | buy, sell, dividend, interest, fee, split, or transfer. |
quantity |
yes | Plain decimal. For a split, the shares added. For an option, contracts. |
price |
depends | Required for every side except split, where it must be blank. For an option this is the per-share price — the 100× contract multiplier is applied separately. |
fees |
no | Plain decimal. Blank means 0. |
amount |
depends | Signed cash impact. See Leaving amount blank below. |
note |
no | Free text. Quote it if it contains a comma, a quote, or a newline. |
underlying, expiry, strike, right |
depends | Required together, and only when type is option; blank otherwise. expiry is YYYY-MM-DD; right is call or put. |
seq |
no | Integer. Left blank, the importer assigns one so that otherwise-identical rows (two same-day margin-interest charges, say) each get their own id. Set it explicitly to pin a row to a specific id. |
Formats
These are strict, because a silently misread number is worse than a rejected row.
- Dates are
YYYY-MM-DD, zero-padded, hyphens only — and must be real calendar dates.2024-02-30is rejected. The same rule applies toexpiry. - Numbers are plain decimals: an optional leading
-, digits, an optional.and more digits. No thousands separators, no currency symbols, no percent signs, no scientific notation, no leading+, no bare.5, no trailing1.. This coversquantity,price,fees,amount, andstrike. seqis a whole number.- Enum values are lower-case and exact.
Buy,BUY, andETFare all errors; writebuyandetf. - Whitespace around a value is trimmed everywhere except in
note, which is taken exactly as written. - Symbols are stored as you write them. Only your manual entries are uppercased for you; the CSV path is not, so pick one spelling and keep it.
Leaving amount blank
amount is the row's signed cash impact, so an account's net cash flow is
exactly the sum of its amount column. Left blank, it is computed for you —
multiplier is 100 for an option row and 1 otherwise:
side |
Blank amount becomes |
|---|---|
buy |
-(quantity × price × multiplier + fees) |
sell |
quantity × price × multiplier − fees |
fee |
-fees |
split |
0 |
dividend |
Row error — must carry an explicit, positive amount. |
interest |
Row error — must carry an explicit, non-zero amount. |
transfer |
Row error — must carry an explicit amount. |
Write the amount yourself and it is used as-is, with three sanity checks that
apply either way: a dividend must be greater than zero, an interest row must
not be zero, and a split must be exactly zero.
Cash rows
Leave symbol blank for activity that only moves cash. Don't invent a
placeholder ticker — a symbol on any row becomes a tracked instrument and gets a
price fetched for it every day.
side |
What it's for |
|---|---|
transfer |
A deposit, a withdrawal, or a journal between accounts. |
interest |
Signed: negative is margin interest charged, positive is interest earned. |
fee |
An account-level fee that isn't attached to a holding. |
For an account whose history you're only importing part of, record the starting
cash as a transfer row dated before the first trade. Cash rows are not
decoration: a holdings account's value is its open positions plus its cash,
and a negative cash balance shows up as a margin debit that reduces the
account's value. See Portfolio and cash.
Options and splits
An option row needs type set to option and all four of underlying,
expiry, strike, and right filled in. Any one of those four on a
non-option row is an error. quantity is in contracts, price is per share,
and the ×100 contract multiplier is applied for you — it is never a column.
A split row records the shares added, not the post-split total: a 4-for-1
split of 10 shares is quantity 30. Its price must be blank and its amount
blank or 0.
Keeping rows distinct
Every trade gets a deterministic id from its account, date, symbol, side,
quantity, price, and seq. Two rows that agree on all of those are the same
trade — which is what makes re-importing a file you've already imported a no-op
rather than a duplicate.
Note what is not in that list: amount, fees, and note. Two identical
cash rows on the same day — two margin-interest charges, two equal deposits —
would otherwise collide, so the importer fills in a blank seq for you, taking
the lowest number not already claimed within that file. Set seq yourself when
you want a row pinned to a specific id. If two rows in one file end up claiming
the same id, the second is rejected with a message telling you to give it a
different seq.
One consequence worth knowing: that assignment is per-file and positional, and
it counts only within the file — never against what's already stored. Strip rows
out of the middle of a file and re-import it, and the rows that shared an
identity with them slide down onto ids already in your history, where they're
skipped as duplicates. Since amount, fees, and note aren't part of the id,
the figures that stay are the deleted rows', not the surviving ones'. If you
need to re-import part of a history, export it first — an export always writes a
concrete seq, which pins each row's identity to the file rather than to its
position in it.
Errors
A bad row is reported on its own line and never aborts the rest of the file. You see it in the preview before anything is written, with its line number and what went wrong.
Only three problems reject the whole file:
- It's empty.
- Its header doesn't match the line above, exactly. The dialog shows you the header it expected next to the one your file starts with.
- It isn't text at all — usually a spreadsheet, which you'll need to export
as CSV first. You can also land here by pasting text that was mangled on its
way through the clipboard; upload the
.csvfile itself instead and the importer will work the encoding out from the bytes.
A worked file
Every side, an option, a split, cash rows, and a quoted note with an embedded quote:
date,account,symbol,type,side,quantity,price,fees,amount,note,underlying,expiry,strike,right,seq
2024-01-05,Brokerage,AAPL,,buy,10,150.5,1.25,,,,,,,
2024-01-06,Brokerage,MSFT,equity,sell,5,300,,,,,,,,
2024-01-07,Brokerage,AAPL,equity,dividend,0,0,0,12.34,Q1 dividend,,,,,
2024-01-08,Brokerage,,,interest,0,0,0,5.5,,,,,,
2024-01-09,Brokerage,,,fee,0,0,9.99,,"Maintenance fee, incl. ""overdraft"" surcharge",,,,,
2024-01-10,Brokerage,,,transfer,0,0,0,-500,"Wire to ""Checking"" acct",,,,,
2024-01-11,Brokerage,AAPL,equity,split,10,,0,,4:1 split,,,,,
2024-01-12,Retirement,AAPL,option,buy,2,5.25,1.5,,,AAPL,2024-06-21,150,call,0
2024-01-12,Retirement,BTC,crypto,sell,0.5,40000,20,,,,,,,1
And a cash-only file — an opening balance, deposits and withdrawals, both legs of a journal between two accounts, two same-day margin-interest charges, and an account fee:
date,account,symbol,type,side,quantity,price,fees,amount,note,underlying,expiry,strike,right,seq
2024-01-02,Brokerage,,,transfer,0,0,0,25000,Opening cash balance,,,,,
2024-02-01,Brokerage,,,transfer,0,0,0,5000,ACH deposit,,,,,
2024-02-15,Brokerage,,,transfer,0,0,0,-1200,ACH withdrawal,,,,,
2024-03-01,Brokerage,,,transfer,0,0,0,-10000,Journal to Retirement,,,,,
2024-03-01,Retirement,,,transfer,0,0,0,10000,Journal from Brokerage,,,,,
2024-03-31,Brokerage,,,interest,0,0,0,-187.97,Margin interest,,,,,
2024-03-31,Brokerage,,,interest,0,0,0,-9.09,Margin interest,,,,,
2024-04-01,Brokerage,,,interest,0,0,0,3.42,Cash sweep interest,,,,,
2024-04-30,Brokerage,,,fee,0,0,25,,Account maintenance fee,,,,,
What an export looks like
Export CSV — in the import dialog for every account, or on an account's
trades screen for just that one — writes the same fifteen columns in the same
order, sorted by date, account, symbol, and seq. Numbers come back with
trailing zeros stripped (1.50 becomes 1.5), and everything is spelled out:
a type you left blank and an amount that was computed are both written
explicitly. The only deliberate blank in an export is price on a split row;
every exported row carries a concrete seq.