Import with an AI assistant
Almost no brokerage exports a file this app can read. What every brokerage does export is something — a CSV with its own column names, a PDF statement, a page you can select and copy — and turning that into one of this app's two formats is exactly the kind of dull, rule-bound reshaping an AI assistant is good at.
This page is how to get one to do it reliably. There are two ways in, and which one you take depends on your assistant, not on your data.
Choose your path
| If your assistant… | Then |
|---|---|
| can connect to tools over MCP — Claude Code, Claude Desktop, and other MCP clients | Connect it to Investment Analyzer and just ask. Skip the prompts entirely. |
| can't — a plain chat window, or one you'd rather not connect to your data | Use the copy-paste prompts below to get a file, then import it yourself. |
One rule holds on both paths, and it is worth saying to the assistant out loud: its job is to map and reformat, never to invent. A row it can't confidently place should come back to you as a question, not as a plausible line in the file. Everything below is written to keep it in that lane.
Path A: a connected assistant
Connecting is a one-time setup, described in the Connect your AI assistant guide — it's per-client, and takes a few minutes.
What changes once it's connected is more than convenience:
- You don't teach it the format. The server does. A connected assistant can ask the app for its own how-to on recording trades or updating balances — the exact header row included — so there is nothing for you to paste. Give it the outcome instead: "Here's my broker's 2023 history. Import the trades into my Brokerage account."
- It can look before it writes. It can list your accounts, so it spells the account name right without asking, and can tell a holdings account from a balance-tracked one — which is what decides which of the two formats your data belongs in at all.
- It can rehearse the import. Both importers take a dry run: nothing is written, and the same per-row report the app's preview shows comes back into the conversation. So when three rows fail, the assistant sees the errors itself and fixes them. The fix-it loop below still happens — you just aren't the one ferrying error text between two windows.
- No file changes hands. The CSV is passed as text, so there is no upload and no download. Practically that means your assistant needs the history as text too: pasted into the conversation, or in a file it can read for itself.
Two things don't change on this path. An import never creates accounts — a connected assistant can create one first if you ask it to, but somebody has to decide the account exists. And the no-inventing rule is still worth saying out loud: anything ambiguous — a corporate action, a cash-in-lieu stub, an option assignment — should come back to you as a question rather than as a row it had a go at.
Path B: copy-paste prompts
Three prompts, and you'll normally use two: one that produces the file, and the fix-it one when the preview finds something.
Each carries the whole format spec, so it works in a fresh chat with no other context. Your assistant doesn't need to have read this page, or know anything else about the app.
Prompt: trades CSV
For a holdings-tracked account — a brokerage, an IRA, a crypto account: anywhere the value comes from what you bought and sold. Paste the prompt, then your broker's history underneath it.
You are converting a brokerage transaction history into a CSV file for an app
called Investment Analyzer. Convert only what I give you. Never invent,
estimate, or fill in a transaction that isn't in my data.
Before you start, ask me for the exact account name — as it appears in my
Investment Analyzer accounts list — unless I've already told you, and confirm
with me that it's an account whose value comes from its trades (a brokerage, an
IRA, a crypto account) rather than one where I state the balance directly.
Don't guess the name from my broker's label. The account has to exist already;
importing never creates one.
Output rules
- Output ONLY the finished CSV, in a single code block, with no commentary
inside it.
- After the code block, list any source rows you could not convert confidently,
each with the reason. Leave them out of the CSV rather than guessing.
The file
- The first line must be exactly this and nothing else:
date,account,symbol,type,side,quantity,price,fees,amount,note,underlying,expiry,strike,right,seq
- Every other line must carry all 15 fields in that order, including the empty
trailing ones. A row that stops early is rejected.
- Comma-delimited, RFC 4180 quoting: wrap a field in double quotes only if it
contains a comma, a double quote, or a newline, and double any inner quote.
- Never put a newline inside a field. If a source value runs over two lines,
collapse it to a single space. The import reports problems by row number, and
a field that wraps makes those numbers stop matching the lines I can see.
Formats — strict, because a rejected row is better than a misread number
- Dates are YYYY-MM-DD, zero-padded, and must be real calendar dates.
- Numbers are plain decimals: an optional leading -, then digits, then
optionally a . and more digits. No currency symbols, no thousands separators,
no leading +, no (1,234) for a negative, no scientific notation, no bare .5
(write 0.5), no trailing decimal point (write 12, not 12.).
- Values from a fixed list are lower-case and spelled exactly. Buy, BUY and ETF
are all errors.
Columns
- date: the transaction date.
- account: the account name I gave you. Case and surrounding spaces don't
matter; everything else must match.
- symbol: the ticker, spelled the way I use it. Leave it blank ONLY on
transfer, interest and fee rows. Required on buy, sell, dividend and split.
Never invent a placeholder ticker such as CASH for a cash row.
- type: one of equity, etf, fund, crypto, option. Blank means equity. A coin
must be typed crypto and keep its plain ticker (BTC, ETH, SOL) — never a pair
spelling like BTCUSD.
- side: one of buy, sell, dividend, interest, fee, split, transfer.
- quantity: required on EVERY row, and always a POSITIVE count — the side
carries the direction, never the sign. If my broker exports a sell as -8
shares, write 8. Use 0 on a dividend and on the cash rows (transfer,
interest, fee). For a split, the shares ADDED, not the new total — a 3-for-1
split of 40 shares is 80. For an option, the number of contracts.
- price: required on every row EXCEPT split, where it must be blank. Use 0 on a
dividend and on the cash rows. For an option, the per-share price — the
×100 contract multiplier is applied by the app, and is never written here.
- fees: blank means 0.
- amount: the row's signed cash impact.
Leave it BLANK on buy, sell, fee and split rows. Blank is not laziness, it's
safer: an amount you write is stored exactly as written and is never checked
against quantity × price, so a mistake in it is invisible. Blank becomes,
where the multiplier is 100 on an option row and 1 otherwise:
buy -(quantity × price × multiplier + fees)
sell (quantity × price × multiplier) - fees
fee -fees
split 0
Write it out on the other three, and nowhere else:
dividend greater than zero
interest non-zero, and signed — negative is margin interest charged to me,
positive is interest earned
transfer signed — positive for money in, negative for money out
- note: optional free text.
- underlying, expiry, strike, right: fill in all four when, and only when, type
is option; leave all four blank on every other row. expiry is YYYY-MM-DD, and
right is call or put.
- seq: a trade's identity is its account, date, symbol, side, quantity, price
and seq — nothing else. Leave seq blank when those first six make the row
unique within the file; the app then fills it in.
But when two or more rows share ALL SIX (two equal deposits on one day, two
margin interest charges, two identical dividends), number those rows
explicitly 0, 1, 2… in the order they appear. The app's own numbering counts
only within the file, so it moves when the file is edited — an explicit seq
pins each row's identity to the file itself, which is what lets me fix one
row later and re-import without disturbing its siblings.
Here is my history:
[paste your broker's transaction history here]
Prompt: balances CSV
For a balance-tracked account — a savings account, a pension, a mortgage, a property valuation: anywhere you state the value rather than derive it. If you're not sure which kind an account is, Accounts has it.
You are converting account statement values into a CSV file for an app called
Investment Analyzer. Convert only what I give you. Never invent a balance, and
never interpolate one for a date I didn't state.
Before you start, ask me for the exact account name — as it appears in my
Investment Analyzer accounts list — unless I've already told you, and for
whether it's a liability. This format is only for balance-tracked accounts: a
savings account, a pension, a mortgage, a property valuation. A row naming an
account whose value comes from its trades is rejected.
Output rules
- Output ONLY the finished CSV, in a single code block, with no commentary
inside it.
- After the code block, list anything you could not convert confidently, with
the reason. Leave it out of the CSV rather than guessing.
The file
- The first line must be exactly this and nothing else:
name,date,amount
- Every other line carries all three fields.
- Comma-delimited, RFC 4180 quoting: wrap a field in double quotes only if it
contains a comma, a double quote, or a newline, and double any inner quote.
An account name with a comma in it needs this.
Formats — strict, because a rejected row is better than a misread number
- Dates are YYYY-MM-DD, zero-padded, and must be real calendar dates.
- Numbers are plain decimals: an optional leading -, then digits, then
optionally a . and more digits. No currency symbols, no thousands separators,
no leading +, no (1,234) for a negative, no scientific notation, no bare .5
(write 0.5), no trailing decimal point (write 12, not 12.).
Columns
- name: the account name I gave you. Case and surrounding spaces don't matter;
everything else must match.
- date: the statement date.
- amount: the account's WHOLE value at the end of that day, cash included.
Blank is not allowed; there is nothing to compute it from.
The sign follows one rule and one rule only. If I told you the account is a
liability — a mortgage, a card, a loan — the amount must be NEGATIVE, even
when the statement prints the balance as a positive "principal balance", so
flip it. Otherwise copy the sign the statement gives exactly as it stands.
One row per account per date. If two rows name the same account and the same
date, the later one wins, so don't emit both unless I asked for a correction.
Here are my statement values:
[paste them here]
Prompt: fixing the rows that failed
The import preview rejected some rows from the CSV you produced. Here are the
errors it reported, each with its line number — the header counts as line 1:
[paste the failed lines from the preview]
Correct only those lines. Keep every other line of the file byte for byte as it
was: don't reorder anything, don't re-derive rows from the original history,
and don't drop a row unless the fix is that it shouldn't be there — say so if
it is. Return the whole corrected file in a single code block.
Those instructions look fussy and aren't, and the mechanism behind them is worth a minute before you run the loop a second time.
A trade's identity is its account, date, symbol, side, quantity, price and
seq — and a row that leaves seq blank gets the lowest number that identity
hasn't already claimed within that file. The count looks at the file, never
at what's already stored. So a row's identity depends on which of its twins sit
above it: delete one of two identical deposits, re-import, and the survivor
slides down onto the deleted row's stored id and is skipped as a duplicate.
Nothing new is written — and because amount, fees and note are not part
of the identity, what stays in your history is the deleted row's figures.
The fix-it loop can walk into this on its own. A row that failed claimed no id
at all on the first pass, so once you correct it, it claims the lowest free
seq — which is the one its identical later sibling took last time. The
correction is skipped as a duplicate, the sibling is written a second time, and
the preview reads 1 valid · 1 duplicate the whole way, which looks exactly
like success.
That is why the trades prompt numbers those rows explicitly. With a written
seq, a row's identity is fixed by the file rather than by its position in it,
so a correction lands where it belongs no matter what else moved. See
Keeping rows distinct.
Running the loop
Import in the header opens the dialog from anywhere in the app. Pick Trades or Balances — or don't: both formats are recognised by their header row, so dropping one on the wrong tab switches the tab for you and says so.
Then either way in:
- Paste rows is the natural fit for a prompt's output. Copy the code block, paste it in.
- Upload file is better if your assistant wrote you an actual file: uploads are decoded from their bytes, so UTF-8, UTF-16 and cp1252 all work. A paste that was mangled on its way through the clipboard can't be recovered — upload the file itself instead.
Nothing is written until you confirm. The preview counts the rows and then lists them one per line as Valid, Duplicate, or Error with the reason beside it. Those error lines — line number and message — are what goes into the fix-it prompt.
One failure looks different from the rest. If the whole file is rejected with That file isn't in the trades CSV format, the first line isn't the header row — a column got renamed or dropped, or a sentence of commentary came along above it. The dialog shows you the header it expected above the one your file starts with; paste both into the fix-it prompt.
Re-importing the whole file is the intended move, and the two formats get
there differently. A trade already in your history is skipped rather than
doubled, so fixing three rows and re-importing writes three trades — provided
nothing else about those rows moved, which is what the seq rule above is for.
Balances don't skip anything: a row landing on a date the account already has
replaces it, counted in the preview as N rows replace balances already
stored on those dates. Re-importing identical values is harmless because the
value is the same; what a re-import can't undo is a corrected file overwriting a
snapshot you meant to keep. Read that count before you confirm.
The dialog itself is covered in full by Trades and CSV import, and the rules these prompts encode are the reference pages — Trades CSV format and Balances CSV format. Reach for those when a row is rejected for something the prompt didn't anticipate.
Read the preview yourself
An assistant can produce a file that parses perfectly and is still wrong: a sell recorded as a buy, a date landing in the wrong year, a page of the statement quietly skipped. The preview only tells you the file is well-formed.
The cheapest real check comes after the import, and it isn't reading the rows
back one at a time. A holdings account's trades screen shows the account's cash,
and cash is just the amount column added up from the last figure you stated
onward — so comparing that one number against the cash on your statement tests
the whole file at once. Once you have stated a figure, the same strip names the
gap between the two outright. See
Portfolio and cash.
Who can do this
Importing needs editor or owner access, like every other write — on both paths, since a connected assistant acts as you. Viewers can read and export, which is enough to hand a history to an assistant, but not to bring one back in.