02.Accounts & broker import
Twelve built-in broker parsers, the Generic column-mapping wizard for everything else, idempotent re-import, asset-class auto-detection, and the immutable imports audit trail.
Create an account first
Every execution row is keyed by (account_id, symbol, executed_at, side, qty, price). Before you import a single broker CSV, open the launcher (Cmd+K) → Accounts, click + New Account, and pick the broker. The accounts row stores:
- Name — your label (e.g. "Webull · main" vs "Webull · IRA").
- Broker — drives the importer dropdown defaults later.
- Currency —
USDdefault; FX trades convert atexecuted_at. - Asset classes enabled — stocks / options / futures / forex / crypto. Determines which import column maps you'll see.
- Cash starting balance — anchors the equity curve. Set it to the first deposit, not zero, or your drawdown numbers are wrong from day one.
You can rename or merge accounts later (POST /api/accounts/merge). Deleting an account cascade-deletes its executions and re-runs the rollup on its symbols — slow on a large book.
The 12 built-in broker importers
Live in crates/traderview-import/src/brokers.rs. Each parser auto-detects the broker by sniffing the header row (column count, signature column names) so dragging a CSV onto the Import tile usually picks the right parser without you choosing it.
| Broker | What works | What's CSV-only |
|---|---|---|
| Webull | Stocks · options · paper-token live snapshot | — |
| Lightspeed | Stocks · options | Live broker integration is on the roadmap |
| IBKR Flex | Stocks · options · futures · forex · multi-currency | Use Flex query XML or CSV export |
| ThinkOrSwim | Stocks · options · futures · spreads collapsed to legs | — |
| TD Ameritrade | Stocks · options · futures | — |
| Schwab | Stocks · options · post-TD-merger format | — |
| Fidelity | Stocks · options | — |
| ETrade | Stocks · options | — |
| Robinhood | Stocks · options · crypto | — |
| TradeStation | Stocks · options · futures | — |
| DAS Trader | Stocks (intraday-focused) | — |
| TradeZero | Stocks · options | — |
Webull additionally has a live read-only adapter (paste your session tokens into Settings → Webull; tokens stay in memory only, never on disk). The adapter polls tradeapi.webullbroker.com every 5 s and pushes a snapshot to WS /ws/webull. Step 06 covers live streams.
Generic column-mapping wizard
For anything not on the list above, point the Generic parser at any CSV. The wizard reads the header row, samples the first five data rows, and asks you to map each execution column:
symbol— required.side— required. The wizard acceptsBUY/SELL,B/S,LONG/SHORT,+/-, or signed quantity.qty— required. Negative qty is interpreted as a sell if no side column was mapped.price— required. The wizard catches per-contract vs total-cost columns by sampling magnitudes.executed_at— required. Parses ISO-8601 + 18 common broker formats (MM/DD/YYYY HH:mm:ss,YYYY-MM-DD HH:MM, …). Timezone defaults to your local TZ, override per-mapping.fees— optional. If a single fee column, it's stored verbatim. If split (commission, exchange, SEC, TAF), the wizard sums them.broker_order_id— optional but recommended; it's the dedupe key for re-imports.
The mapping is saved per-broker per-user — the next CSV with the same header signature is parsed without showing the wizard.
The import pipeline — what runs server-side
- Raw bytes hashed (sha256) and recorded in
imports. The original CSV is preserved as a blob for audit / re-parse. - Parser dispatches by broker enum (or via Generic with the saved mapping).
- Asset class auto-detected from the symbol pattern:
- Options: OCC-style
AAPL 240119C00185000orSYMyyMMddCNNNN. - Futures:
=F//ES/ESM4. - Forex:
=X/EURUSD6-letter pair. - Crypto: known ticker list +
-USDsuffix. - Stock: fallback if nothing matches.
- Options: OCC-style
- Each row inserts into
executionswithON CONFLICT (account_id, broker_order_id, executed_at, symbol, side, qty, price) DO NOTHING. Re-importing the same CSV is idempotent: zero new rows. - FIFO roll-up re-runs for every affected
(account_id, symbol)pair viatraderview-core::rollup. Thetradestable is rewritten for those pairs; everything else is untouched. - Toast back to the UI with rows-added / rows-skipped + a link to the affected accounts.
The audit trail you can always rebuild from
The imports table is immutable. Every CSV you've ever uploaded sits there with the original bytes, a content hash, the parser version, the user who uploaded, and the timestamp. From the Admin → Imports tile you can:
- Re-parse — useful when a broker changes their CSV format and the parser is updated. Re-parse drops the old executions for that import and re-inserts under the new logic.
- Delete + cascade — drops the executions, re-runs the rollup. The raw bytes stay (set a tombstone flag); the historical trades disappear.
- Diff against the current state — shows which executions still survive vs which got merged / split by the rollup. Useful for "did my last import break anything?" forensics.
POST /api/trades/rollup) after a hand-edit of executions. The auto-run only fires on import / new execution; manual SQL touches skip it.
Multi-currency & options gotchas
- IBKR Flex multi-currency. If your account currency is USD but trades happen in EUR / CAD / JPY, the importer keeps the trade-currency price in
executions.priceand stores the FX rate inexecutions.fx_rate. The rollup converts to account currency for P&L; the original is preserved for tax-lot per-currency reporting. - Options contract multiplier. Stored on the
executionsrow (typically 100 for equity options, but the parser honors broker-supplied multipliers for index / FX options). P&L uses the row's multiplier, not a global constant. - Spreads. Most brokers report a vertical / butterfly / iron condor as multiple leg rows with a shared
broker_order_id. The importer keeps them as separate executions; the rollup rolls each leg's trade independently. The journal view groups bybroker_order_idso you see the spread as one unit. - Wash sales. Wash-sale adjustments come in on a separate import (broker year-end 1099-B for stocks; broker tax summary for options). The tax-lot tracker in step 14 reads them.
- Expired / assigned options. The
POST /api/trades/close-expired-optionsendpoint closes positions that expired worthless or were assigned. Run it once after option expiration Friday.