Skip to main content

Calculations

All analysis is rule-based (no LLM), pure and covered by tests.

Net worth​

portfolio (investments) + off-portfolio assets (real estate, cash) βˆ’ debt (loans)

Assets and positions held in a foreign currency are converted to PLN at the current NBP rate (table A); the conversion refreshes when the app starts, and further calculations then operate on amounts already in PLN.

Cryptocurrencies. A crypto position with a quantity and a recognised symbol is valued at the spot rate from CoinGecko (public API, no key), just like currencies against the NBP: value = quantity Γ— rate, with the rate cached (and refreshed) and a fallback to the last known price when there is no network. The average purchase price = capital Γ· quantity; in advanced mode the quantity, the weighted average (WAC) and the realised result are derived from the transaction history. Privacy: the request for a public rate contains no data about you or how much you hold β€” it is purely a market-price lookup.

Gold and metals. A metal position holds grams of pure metal (weight Γ— fineness/1000). Gold is valued at the NBP price (cenyzlota β€” the official price of 1 g of 1000-fineness gold in PLN, published once per business day; the same free API the currency rates come from): value = pure grams Γ— price per gram. The valuation refreshes on app start; a very old cache (offline) never overwrites anything. Silver, platinum and palladium are valued from the Stooq spot (XAG/XPT/XPD, USD per troy ounce) converted at the NBP rate into zΕ‚oty per gram of pure metal. Gain/loss is measured against the purchase price entered when adding. A monthly contribution to gold settles in grams (amount Γ· the month's NBP price; past months come from the NBP archive, the positional median of daily quotes) and lands in the position's purchase history β€” just like crypto contributions; the position's initial holding is protected by a seed entry in the history.

Retail treasury bonds (EDO/COI/TOS/OTS/ROR/ROS…). A position with series terms is valued by the bond engine: year 1 at the issue rate, years 2+ = CPI (GUS, y/y) + margin (deflation β†’ margin only), with annual capitalization (EDO/TOS/ROS/ROD) or interest payouts (COI yearly, ROR/DOR monthly; payouts don't raise the position value β€” they're cash flow). Within a period interest accrues linearly by days. CPI comes from a bundled GUS table; months outside it use the position's editable inflation assumption. The early-redemption fee (zΕ‚/unit, per series) is capped at accrued interest. Simplification: ROR/DOR use the purchase rate (the NBP reference rate has no free CORS feed). Monthly contributions skip bond positions β€” buying more in another month is a different series, i.e. a new position from the catalog.

Quantity computed from contributions (automatically). In the monthly entry you don't enter how many units you bought β€” you provide the contribution amount in zΕ‚oty, and the app converts it into units at the coin's rate for that month:

units = contribution amount (PLN) Γ· coin's rate for the given month (PLN)

The sum across months = the entire holding. We take the rate live for the current month, and for past months β€” from the CoinGecko archive (/coins/{id}/history, also public, no key). Each such contribution is saved as a transaction in the history (a computed purchase), so the quantity, the average price (WAC) and the value are calculated by the existing code. Saving the same month again overwrites the entry (no double counting), while manually entered transactions remain untouched. When the rate for a month cannot be fetched (offline / unmapped coin), the contribution falls back to a plain zΕ‚oty top-up β€” we never block the save and never invent a quantity.

Savings rate​

The average over recent months with income: contributions / income.

Financial health score (0–100)​

A weighted composition of components:

ComponentWhat it measuresWeight
Buffermonths of expenses in the reserve (target 6)25%
Savings rateagainst the 20% target20%
Debtthe lower the debt share, the better15%
Diversification1 βˆ’ the portfolio's Herfindahl index15%
Goalsaverage goal progress10%

The score is the sum of value Γ— weight normalised to the available components.

FIRE​

The FIRE portfolio = the investment lens (accounts + cash + rentals), not total wealth (your home/car are left out). We treat the rates as real (after inflation).

  • Freedom amount (FIRE number) = (annual expenses βˆ’ fixed post-FIRE income) Γ· SWR. Multiplier = 1/SWR (4% β†’ Γ—25; 3.5% β†’ Γ—28.6; 3% β†’ Γ—33).
  • % FIRE = investment portfolio / freedom amount.
  • SWR configurable (default 3.5%); monthly /12.
  • Coast FIRE = the freedom amount discounted to today at the real return over the years to retirement: target Γ· (1+r)^years. "You already have enough that, without adding more, it will grow to the target."
  • Years to freedom β€” iteratively: capital Γ— (1+r) + annual contribution, until β‰₯ target.
  • Levels Lean / Full / Fat = 0.7Γ— / 1Γ— / 1.5Γ— annual expenses.
  • Monte Carlo β€” N draws of annual returns ~N(mean, volatility); "chance of success" = % of runs that reach the target (deterministic with a seed).
  • Effective after-tax SWR β€” SWR Γ— (1 βˆ’ weighted average account tax: IKE 0, IKZE 10%, standard 19%). A directional simplification, not advice.

Return on investment​

We show three numbers (Analysis β†’ Health):

  • Total gain = current value βˆ’ invested capital (absolute, in zΕ‚).
  • Market return (TWR) β€” time-weighted: a chain of sub-period rates between snapshots with contributions taken out of each period. It answers "how did the market/strategy perform", independently of contribution timing. Showing TWR and MWR side by side is the industry standard: TWR judges the strategy, MWR β€” your actual outcome.
  • Annual return (MWR) = money-weighted (XIRR) β€” the internal rate of return computed from cash flows: the oldest portfolio snapshot is the starting capital, subsequent contributions are added in their respective months, and the current value is the end state. This way the number accounts for how much and when you contributed β€” which the absolute gain alone doesn't capture (e.g. a large contribution just before a rise inflates the % gain but not the annual return). Computed monthly (Newton + bisection); requires at least 2 months of history, otherwise we show "β€”".

Value projection​

Future value = target contributions + compounding at the annual rate assumed for the position (e.g. ETF and IKE 8%, EDO 5.5%, ROS and COI 5.25%, PPK 6%). Debt is reduced by planned overpayments (conservatively, ignoring interest).

Crypto is an exception: we don't compound a fixed rate (it's not a deposit). The crypto projection is flat β€” current value + any contributions, with no assumed growth. In the projection table a crypto position shows "current value Β· no forecast" with a variable badge, and the editor has no "target" or "rate of return" fields (crypto is valued from the current rate).

Employer match (US 401(k) employer match). For a 401(k) account, in the editor you can set how much the employer adds to your contribution: a percentage of your monthly contribution (e.g. 50%) and an optional monthly match cap. Then the effective monthly contribution = your own + the match β€” and that is what compounds in the projection (the employer's capital grows too). The match counts as a contribution (capital), not as gain, so it doesn't inflate the result. With no match set, the projection stays unchanged (zero impact on the other accounts).

Tax on withdrawal β€” by account type​

We compute net worth depending on the account type (tax wrapper), not on a fixed basis. We keep the rules as data (limits and multipliers change every year):

  • IKE, OIPE, PPK, PPE β€” a qualified withdrawal is tax-free (net = gross).
  • IKZE β€” 10% of the entire withdrawal (not just the gain). The contribution lowers PIT in the year it is made β€” we show that relief separately (we don't add it to net worth), and only when you provide your PIT rate (we don't guess it).
  • Standard account / bank β€” Belka 19% on the gain (gain = value βˆ’ contributions βˆ’ capital).

Positions with no assigned account type behave as before (the "taxable" flag = Belka), so the projection of existing plans doesn't change.

Other countries (per-country engine)​

The same engine computes the tax by country (country) β€” you choose it in Settings:

  • πŸ‡©πŸ‡ͺ Germany β€” Depot: Abgeltungsteuer 26.375% on the gain after Teilfreistellung (a portion exempt depending on the instrument) and the Sparerpauschbetrag allowance, plus an optional Kirchensteuer; RΓΌrup/Riester/bAV β€” a deduction on the way in and a taxed portion of the annuity (Besteuerungsanteil).
  • πŸ‡¬πŸ‡§ United Kingdom β€” GIA: CGT 18/24% on the gain after the annual Β£3,000 allowance (a separate Β£500 dividend allowance); ISA/SIPP tax-free; the tax year 6 April–5 April (not the calendar year) affects how the limits are counted.
  • πŸ‡ΊπŸ‡Έ USA β€” brokerage: LTCG 0/15/20% depending on the holding period; Roth vs Traditional as a toggle (tax-free on the way out vs deductible on the way in); 529 maps to a child's education goal.
  • πŸ‡«πŸ‡· France β€” CTO: PFU 31.4% split into the components PS 18.6% + IR 12.8%; PEA (0% IR after 5 years) and assurance-vie (after 8 years) computed by account tenure (opening date); PER β€” a deduction on the way in.

Projections don't fake a precision they don't have (e.g. the Vorabpauschale is explicitly not accounted for) β€” simplifying assumptions are flagged.

Annual contribution limit​

For accounts with a limit (IKE/IKZE/OIPE, DE RΓΌrup/Riester/bAV, UK ISA/SIPP, US 401(k)/IRA/HSA, FR PEA/PER) we track the use of the annual limit β€” the sum of a given person's contributions to accounts of that type in the year against the limit. IKZE has a higher limit for people running a business (JDG). In the UK the "year" is the tax year (6 April). Accounts with no tracked limit (PPK/PPE, GIA, brokerage, CTO…) don't show the bar.

Horizon and grouping​

In Portfolio we show the projection for a single chosen horizon (2/5/10/20 years) instead of four columns β€” no sideways scrolling. Rows can optionally be grouped by person or account type.

Insights​

The rules generate tips with a severity of good / info / warn: loan payoff, savings-rate trend, buffer, debt share, portfolio concentration (and crypto risk), % FIRE. The same rules power your notifications.