taxjson

Open source · Canada and US · runs on your machine

Your broker's CSV in. Schedule 3 out.

taxjson computes capital gains the way the CRA and IRS actually define them: one average-cost pool per security across all of your accounts, the superficial-loss rule checked against your RRSP and TFSA too, options premiums folded where IT-479R says they go, splits, spin-offs and mergers handled with elections you choose. Nothing leaves your computer.

bash -c "$(curl -fsSL https://taxjson.com/install.sh)"

One line on Linux or macOS. Then taxjson init --country canada (or usa) in an empty folder, drop your exports into inputs/, and taxjson run.

taxjson sum
REALIZED-GAINS SUMMARY — CAD, tax year 2026, basis: wash-adjusted

TAXABLE ACCOUNTS
ACCOUNT        STOCK     OPTION   REALIZED   DIVIDEND      FEES
margin     18,204.10   2,310.00  20,514.10   4,120.55    412.30
crypto      1,975.42       0.00   1,975.42       0.00     61.18
--------------------------------------------------------------
SUBTOTAL   20,179.52   2,310.00  22,489.52   4,120.55    473.48

NOTE: 2 superficial loss(es) denied — $1,236.40 deferred
      into the RRSP repurchase (see wash_radar_margin.rpt)

How it works

Three steps, all on disk

Export

Download the activity CSV from each broker — IBKR Flex, Questrade, RBC Direct Investing, Webull, Kraken, Coinbase — and drop it into inputs/<account>/. Registered accounts are declared as sheltered in one config file.

Run

taxjson run parses every file, converts at the Bank of Canada rate for each trade date, pools identical securities, applies splits and corporate actions, then runs the superficial-loss pass across taxable and registered accounts.

Read

Per-account summaries, the wash radar ("safe to sell at a loss?"), harvest candidates, T1135, a tax estimate, and form-ready exports for Schedule 3 or Form 8949 land in reports/. Every number traces back to the rows that produced it.

Tax year, country, province and base currency live in taxjson.toml. Manual history goes in plain-text .tt files for anything a broker didn't export — an old position, a transfer in, a custody move.

Rules with receipts

Every rule cites its source

Tax software usually asks you to trust it. taxjson ships REFERENCES.md: each computation names the section of the Act and the CRA or IRS document it follows — and each thing it deliberately does not do names the provision it skips.

Superficial loss, across accounts

s.54 / s.40(2)(g) / s.53(1)(f), with the RRSP and TFSA treated as affiliated persons (s.251.1). A loss denied because the plan repurchased is reported as permanently denied, not quietly deferred.

Identical properties

s.47 average cost, IT-387R2: a US listing and its TSX listing are the same pool; a CDR is not. Trades settle on the settlement date, as T4037 says.

Options and assignments

IT-479R: premium on an assigned put reduces the shares' cost; on an exercised put it reduces the proceeds; expiries are losses of the premium.

Corporate actions

Splits, spin-offs (s.86.1), mergers with the s.85.1 rollover election, return of capital, reinvested distributions — with the election recorded and the filing reminder printed at every run.

US wash sales

IRC §1091 with the Reg. 1.1091-1 matching, §1223(3) holding-period tacking, Rev. Rul. 2008-5 for IRA repurchases, Rev. Rul. 66-7 for the one-year test.

Crypto

Property, per the CRA guide: every swap, spend and gift is a disposition at fair value; identical-property pooling and the superficial-loss rule apply per coin.

Verification

Checked against the one thing that can't lie: your broker's positions

Internal reports can all agree and still be wrong. taxjson sanity compares the positions the books say you hold against the positions your broker says you hold, account by account. Any difference is either a trade that hasn't reached the CSV yet or a booking problem — there is no third kind. taxjson run finishes with that check.

2,100 tests

Every fix lands with a regression test that failed before it. No mocks for tax math.

Three property fuzzers

Conservation of gains under the superficial-loss rule, transfer-netting, and split-during-settlement books, thousands of random ledgers per run.

Published limits

KNOWN_ISSUES.md lists what is out of scope and what to do by hand, with the citation.

Brokers

Reads the exports you already have

Interactive Brokers (Flex / activity)QuestradeRBC Direct InvestingWebullKrakenCoinbasePlain-text .tt for anything else

Questrade and IBKR Flex statements can be fetched directly with taxjson fetch. Every parser accounts for every row it reads: rows it recognises but doesn't translate are counted and named, never silently dropped.

Install

Your first books in ten minutes

bash -c "$(curl -fsSL https://taxjson.com/install.sh)"

Installs the latest release into ~/.local/share/taxjson with its own Python environment and links taxjson into ~/.local/bin. Re-run to upgrade. Needs git and Python 3.9+; nothing else. TAXJSON_CHANNEL=dev tracks the development branch.

mkdir -p ~/taxes/2026 && cd ~/taxes/2026
taxjson init --country canada   # or usa; writes taxjson.toml and inputs/
# drop broker CSVs into inputs/margin/, inputs/rrsp/, …
taxjson run                  # books, reports, wash pass, sanity check
taxjson sum                  # the realized-gains summary
taxjson wash-radar           # "can I sell this at a loss today?"

Prefer a clone? git clone https://github.com/taxjson/taxjson && cd taxjson && scripts/dev-setup.sh. The full pipeline reaches the Bank of Canada (FX rates) and, for unpriced crypto rows, Yahoo Finance; TAXJSON_OFFLINE=1 forbids both. No broker credential is ever stored or sent anywhere but that broker.

Frequently asked

Before you file

taxjson is a calculator, not tax advice. It applies published rules to the rows you give it and shows its work; the return is yours, and a professional should review anything unusual — elections, cross-border reorganisations, business-income questions.
Does it send my data anywhere?

No. Parsing, pooling and the wash pass all run locally. The only network calls are FX rates from the Bank of Canada and crypto prices from Yahoo Finance for rows the export didn't price, and both can be disabled. SECURITY.md lists every egress.

Canada or US?

Both engines ship. Canada is average-cost ACB with the superficial-loss rule and settlement-date timing; the US engine is FIFO with §1091 wash sales, short/long-term classification and Form 8949 codes. One project, one country.

What about the tax years I already filed?

taxjson filed locks the numbers you filed; later runs flag any drift so an engine fix or a late row shows up as a candidate for an adjustment rather than a silent change.

My broker isn't listed.

Parsers are small, self-contained modules with a row-accounting contract; a new one is a contribution most people can make in an afternoon, and the plain-text .tt format covers you in the meantime.

How is it verified?

A 2,100-test suite that every change must pass, three property fuzzers, an audit log in the changelog, and — the one that matters — taxjson sanity against your broker's own position export.