← All features

Tax

Estimate the tax due on your realized gains and investment income, computed from the transactions you've logged. The engine matches every sale against what you originally paid (cost basis), totals the gains per year, and applies separate rates to capital gains, income (dividends and rewards), and interest, like a lightweight Koinly that runs on your own ledger.

Tax in Krosos

These figures are estimates, not tax advice. The jurisdiction presets are editable suggestions; check the rates and rules that apply to you.

How to use

  1. Open Settings → Tax, pick the Portfolio you're configuring, then its Jurisdiction. The preset pre-fills the cost-basis method, the three rates (capital gains, income, interest), the annual exemption, and (for Belgium) the basis-reset date. Adjust anything, then save. Tax is set per portfolio: each defines its own rules, and a portfolio isn't taxed until you configure it. For a company portfolio, pick Belgium (company), which fills the corporate rate (25%) with no personal exemption.
  2. Open the Tax tab and pick a year. The Estimated tax table breaks the total down by category (capital gains, income, and interest) each as its taxable base × its own rate; under capital gains it shows the build-up (gains − losses − exemption = the balance eligible for tax).
  3. When more than one portfolio is configured, a portfolio filter appears next to the year. Pick one to see its tax at its own rates, or All portfolios to sum them. Where portfolios tax a category differently, that rate shows as mixed but the totals are still exact.
  4. Review the Disposals table: one row per sale, with proceeds, cost basis, and the resulting gain or loss.
  5. Fix anything under Needs attention (see below) so the estimate uses real purchase costs instead of worst-case ones.

Where the numbers come from

Every trade you log carries its € value, so the engine can replay your full history:

Tagging a transaction

Not every trade is a taxable sale. The row menu (⋮) on the History tab offers these tags:

The same row menu (edit, delete, tag) is available on the rows of the Tax tab's Disposals, Dividends, and Interest sections, so corrections can be made where you spot them.

Excluding a whole position

To keep an entire holding out of the tax report (a car, a collectible, anything out of scope), use the eye-off button on its row under Needs attention. Excluded positions live in the Excluded section and can be re-included anytime; their transactions show a "position excluded" marker in History.

Tax exclusion is independent of the Balance sheet's count in P/L toggle: one keeps a position out of the tax report, the other only out of the P/L cards. A line can be excluded from P/L yet fully taxable (a cash account whose interest is income), or shown in P/L while its tax is handled elsewhere.

Unknown purchase costs

Positions that arrived without a price (synced balances, quantity corrections, or holdings imported without an invested amount) have no known cost. They count at zero cost, so the estimate errs high, never low, and the position is flagged with a no basis warning.

Quantity adjustments carry a € value: new incoming adjustments are priced automatically at the ticker's latest cached € price when they're created (sync refreshes, manual quantity edits), so most never warn at all. To set or correct one, use Set € value in the adjustment's row menu (or click its chip), the same dialog as declarations, with the row's date locked in.

The value you enter is the cost basis of those units: what they cost you, not merely their market value. The dialog suggests the market close on the row's date as a convenience, but what to enter depends on how the units arrived:

Tagging an adjustment as income also settles its basis by itself: the tag means "these units were free", so the unknown-cost warning clears even without a value: the units carry a basis of €0 (a later sale realizes the full proceeds) and the income section shows €0 until you price it.

Auto-tag rules save you from tagging every drip by hand. From an adjustment's row menu in History, pick Always tag like this… to create a rule for that exact position: a tag (reward, interest, dividend, or cost) and a quantity ceiling. From then on, any adjustment on that position at or below the ceiling is tagged automatically the moment it's created (including nightly sync refreshes); income tags match units arriving, cost matches units leaving. Creating a rule also sweeps the position's existing untagged adjustments and tags the matches, with an Undo if it caught more than you meant. A manual tag always wins: rules never touch a row that's already tagged. Rules are listed and removable under Settings → Tax → Auto-tag rules; deleting one stops future auto-tagging but leaves the tags it already applied.

To fix it, use Set opening value (from the warning, or the no basis chip in History) and declare what the position was worth on a chosen date. From that date the declared value is the position's cost basis.

The value can be entered per unit (the market price you look up; the engine multiplies by the quantity held on that date) or for the whole position, and in the instrument's quote currency. Foreign-currency values convert to € at the ECB reference rate of the as-of date (falling back to today's rate if the fixing can't be fetched; the hint says which rate was used).

You don't need a warning to declare a value: any holding's row menu on the Balance sheet (⋮ → Set cost basis…) opens the same dialog, defaulting to the reset-photo date. Use it when a position's cost basis is wrong even though it isn't flagged, e.g. a partly-sold holding whose invested/realized figures no longer reflect the remaining units.

Declared values are listed and managed under the Tax tab's Declared values subtab, where each one can be revised or deleted at any time; the report recomputes immediately.

Override the higher-of rule (checkbox in the dialog, shown only when Reset uses cost if higher is on): tick "Use this exact value" to make your figure authoritative. It becomes the cost basis even when it's lower than what the ledger thinks you paid. This is the escape hatch for when the computed acquisition cost is itself wrong (lifetime invested attributed to a partly-sold remainder), so the higher-of rule would otherwise keep the wrong high number. Declarations using it show an exact chip.

Reset uses cost if higher (Settings → Tax, on for the Belgium preset): when a declared reset value is lower than what the ledger says you actually paid, the acquisition cost is used instead, the Belgian transitional rule (valid until end 2030). Positions that were underwater at the photo date keep their full deductible loss.

Click any gain/loss figure (in History or in the Tax tab's Disposals) to see how it was calculated: proceeds, matched cost with per-unit figures, and the gain. The Cost basis row expands to show exactly which lots the cost came from (each buy, opening position, adjustment, or declared photo value, with the quantity, cost, and per-unit price taken from each), along with notes on which basis rule applied (declared photo vs acquisition cost, unknown-cost units, oversells). Every lot has an edit pencil that opens the source row's own editor (the trade, the adjustment, the opening, or the declaration) so a wrong basis can be fixed right where you spot it.

Two details worth knowing: a declared value of 0 is valid (an airdrop or free acquisition has a known basis of zero; the warning and the rows' no basis chips clear), and the declaration only takes effect if you actually held the position on the as-of date. If the units arrived later, Needs attention says so and you should redeclare with a date on or after they arrived.

Opening positions can carry a date. The optional As of field on a holding (Add/Edit holding) slots the opening into the timeline: History shows it at that date, and the tax replay treats it like any other event, so a dated opening that postdates the reset photo correctly keeps its own invested amount instead of the declared value. Left empty, the opening simply predates your whole ledger, as before.

Partly-sold openings. An opening's cost basis is its Invested figure, but Invested is lifetime cost and isn't reduced when you sell, so for a position you've partly sold it overstates the remaining units' basis. The tax engine therefore defaults an opening's basis to Invested − Realized (never understating tax); a position whose proceeds already covered the full cost defaults to a €0 basis and is flagged under Needs attention. The exact remaining-unit basis can't be inferred from a snapshot, so set it precisely with Set cost basis on the opening's row (History) or the holding's row (Balance sheet). This records a tax cost basis that overrides the estimate without touching Invested/Realized, which stay intact for the P/L view.

Good to know

Related