Methodology
How Hedger calculates every number — from raw broker data to tax report
1. Data Pipeline Overview
Every number in Hedger is derived from broker exports through a strict four-layer pipeline. Each layer serves a distinct purpose and can be inspected independently.
| Layer | Model | Purpose |
|---|---|---|
| Vault | UploadedFile |
Stores the original broker export (CSV, XLSX) exactly as uploaded — immutable source of truth. |
| Raw | RawEvent |
One row per broker CSV row. Preserves all original fields plus a parsed canonical type. Never modified after import. |
| Ledger | LedgerEntry |
Normalized double-entry-style records in canonical units. Every entry links back to its RawEvent — full audit trail. |
| Derived | FifoPairing, UnpairedEntry, PositionDaily, CashPositionDaily |
Computed from ledger on demand: FIFO pairings, open positions, daily snapshots, cash balances. Fully rebuildable. |
The derived layer is deleted and rebuilt on every sync. This means any data correction at the vault or ledger level automatically propagates forward — there is no stale derived state.
2. Parsing & Normalization
Each supported broker has a dedicated parser that maps broker-specific column names and formats to Hedger's canonical schema. Parsers handle:
- Date and time parsing (including timezone normalization to UTC)
- Currency and amount sign conventions (some brokers use absolute values with a separate debit/credit column)
- ISIN and ticker mapping
- Broker-specific transaction type codes mapped to canonical types
The 18 canonical ledger entry types are:
buy— purchase of shares or unitssell— sale of shares or unitsdividend— cash dividend paidfee— broker commission or platform feedeposit— cash transferred inwithdrawal— cash transferred outstock_split— share quantity adjustment (no cash)merger— shares exchanged in a mergerspinoff— new shares received from parent company
bond_coupon— periodic interest payment on a bondinterest— cash interest on idle fundswithholding_tax— tax withheld at source on incomestock_grant— shares received as compensation (RSU/ESPP)delisting— forced close when a security is delistedisin_change— same security, new ISIN (continuity preserved)exchange_offer— voluntary exchange for cash or new securitiesrights_issue— subscription right exercisedcash_settlement— cash received in lieu of fractional shares
The original raw CSV row is always preserved. If Hedger's parser makes an incorrect assumption, the raw row can be re-parsed without re-uploading the file.
3. FIFO Matching
Hedger uses First-In, First-Out (FIFO) matching: when you close a position, the oldest open lot is consumed first. This is the method required by Czech tax law.
Opening vs. Closing Types
- Opening:
buy,stock_grant— always create a new open lot - Closing:
sell,delisting— always consume open lots (oldest first) - Corporate actions:
spinoff,merger,isin_change,exchange_offer,rights_issue— determined by quantity sign (positive = opening, negative = closing)
Cross-Broker Mode
By default each broker's positions are tracked independently. In cross-broker mode (opt-in per user), a sell on broker B can be matched against a buy on broker A — useful when you transfer positions between brokers.
Fee Allocation
When a sell closes multiple lots (a lot is split across several closes), the commission is allocated proportionally to the quantity closed from each lot. This gives an accurate per-lot net P&L.
Worked Example
| Date | Action | Qty | Price | Notes |
|---|---|---|---|---|
| Jan 1 | Buy | 100 | $150 | Lot A — 100 shares at $150 |
| Mar 1 | Buy | 50 | $160 | Lot B — 50 shares at $160 |
| Jun 1 | Sell | 120 | $180 | Closes 100 from Lot A, then 20 from Lot B |
FIFO pairing result:
| Lot | Qty Closed | Cost | Proceeds | P&L |
|---|---|---|---|---|
| Lot A (Jan 1) | 100 | $150 | $180 | +$3,000 |
| Lot B (Mar 1) | 20 | $160 | $180 | +$400 |
| Remaining open (Lot B) | 30 shares @ $160 — unrealized | |||
4. TWR — Time-Weighted Return
TWR measures how the portfolio performed independent of when you added or withdrew money. It is the industry-standard metric for comparing a manager's performance against a benchmark.
Daily Sub-Period Returns
For each day t with portfolio value V_t, previous-day value V_{t-1}, and net cash flow CF_t (positive = deposit):
r_t = V_t / (V_{t-1} + CF_t) − 1
Cash flows are added to the denominator so that deposits do not inflate the day's return.
Linking
Daily sub-period returns are geometrically linked (chain-multiplied) over the full period:
TWR = ∏(1 + r_t) − 1
Annualization
If the period spans more than one year:
TWR_annualized = (1 + TWR)^(1 / years) − 1
Data Quality Guard
Daily sub-period returns are capped at [−0.30, +0.30] (i.e., ratio in [0.70, 1.30]) before linking. This prevents a single bad price tick or missing data point from corrupting the entire series.
5. XIRR — Extended Internal Rate of Return
XIRR is a money-weighted (dollar-weighted) return. Unlike TWR, it reflects the timing of your cash flows — if you deposited a large sum right before a market crash, XIRR will be lower than TWR for the same period.
NPV Equation
XIRR finds the rate r that satisfies:
∑ CF_i / (1 + r)^(t_i / 365.25) = 0
Where CF_i is each cash flow (deposits negative, withdrawals and final portfolio value positive) and t_i is the number of days from the first cash flow.
Solver
The equation is solved numerically using Brent's method (bracketed root-finding). This is the same algorithm used by Excel's XIRR function and guarantees convergence when a solution exists.
TWR vs. XIRR
Use TWR to evaluate how good your stock picks were. Use XIRR to evaluate the actual return on your invested capital. The two diverge when you make large deposits or withdrawals at inopportune times.
6. Attribution Model
Every change in portfolio value is decomposed into six independent buckets that sum exactly to the gross period change:
Gross Change = Price + FX + Income + Taxes − Fees + Deposits
Net P&L = Price + FX + Income + Taxes − Fees (Gross minus deposits — your real earnings)
Taxes and Fees are stored with their natural sign (negative for withholding and commissions), so the formula above is a pure sum.
Why no "Trades" category?
A buy or a sell is a SWAP between two buckets — cash decreases by the same amount that stock value increases (or vice versa). On its own a trade produces zero P&L. All real P&L comes from price moves, currency moves, income, and fees. We attribute price/FX movement up to and through the trade execution moment, so a buy at 100 followed by a sell at 110 in the same period shows up as +10 of Price, not as a "Trade".
This is more honest than splitting out a "Trades" line, which often double-counts: the cash leg of a buy gets dumped into one bucket while the corresponding stock leg sits in another, and reconciling them requires a residual that nobody understands.
| Bucket | Definition | Example |
|---|---|---|
| Price | Mark-to-market change on held shares (held the whole period, bought during, or sold during). Per-lot decomposition uses each lot's entry price as the basis. | 100 AAPL rose $5 → +$500 × FX at end. Bought 50 more mid-period at $180; they end at $185 → +50 × $5 × FX. |
| FX | Currency rate impact on every foreign-currency position (stocks AND cash), computed lot-by-lot from each lot's entry FX rate to the period-end FX rate. | USD weakened 2% vs CZK during the period → −2% applied to USD-denominated stock value AND USD cash balance. |
| Income | Dividends + interest + bond coupons + bond accrued + grants + cash settlements + fund distributions + capital returns. Gross, before withholding tax. | AAPL paid $0.24/share dividend on 100 shares → +$24 Income, and −$3.60 Taxes (15% WHT) as a separate line. |
| Taxes | Withholding tax withheld at source on dividends and interest. Tracked as its own bucket — it is reported separately on the CZ tax return as a foreign tax credit against Czech income tax. | US broker withheld $3.60 (15%) of the AAPL dividend → −$3.60 in Taxes. |
| Fees | Standalone broker fees that can't be attributed to a specific trade (exchange fees, inactivity fees, borrow fees, FX conversion costs). Fees bundled into a trade row are absorbed into the trade's effective price and appear in Price instead — a $100 buy with a $5 commission becomes an effective $100.50 entry, so the $5 drag shows up as a negative Price contribution when marked to $100. | IBKR monthly inactivity fee $10 → −$10 in Fees. |
| Deposits | External cash in (deposit) or out (withdrawal). NOT P&L — pure reallocation between your bank and your broker. Excluded from Net P&L but included in Gross. | Wired 50,000 CZK to broker → +50,000 in Deposits, no impact on Net P&L. |
Each bucket is computed daily at the position/cash level and aggregated to any period (daily, weekly, monthly, quarterly, yearly). The sum is exact by construction. Any residual displayed in the UI is from corner cases (grants without cash legs, FX rounding) and is logged as a warning when meaningful.
Distribution
Below the attribution row, the Distribution bar shows how much value sits in each asset class (Stocks — including ETFs/funds — Bonds, Options, Other, Cash). Each slice also shows its period delta, so you can see reallocation between buckets without needing a separate "Trades" line: a buy of 50k of stock shows as +50k Stocks / −50k Cash.
7. Cash Balance Computation
The cash balance for each currency is computed by replaying every cash-affecting ledger entry in chronological order, maintaining a running balance per currency per broker.
Entry Types That Affect Cash
buy— outflow (negative)sell— inflow (positive)dividend,bond_coupon,interest— inflowfee,withholding_tax— outflowdeposit— inflowwithdrawal— outflowcash_settlement— inflow or outflow depending on sign
Types that do not affect cash: stock_split, stock_grant (shares received without cash payment), and pure corporate reorganizations (merger, spinoff, isin_change) when no cash changes hands.
The running balance approach means the cash history is independently verifiable: export the cash ledger from Hedger and compare line-by-line against your broker's cash statement.
Cross-Currency Fees
Fees denominated in a different currency from the trade are tracked separately using a fees_currency field. This ensures the cash balance for each currency is accurate without introducing synthetic FX conversions.
8. FX Conversion
All FX rates come from the Czech National Bank (CNB) daily fixing, published each business day for 30+ currencies. This is the authoritative source for Czech tax purposes.
Cross-Rate Computation
CNB publishes rates against CZK. For non-CZK pairs (e.g., USD/EUR), Hedger derives the cross-rate:
USD/EUR = (USD/CZK) ÷ (EUR/CZK)
Per-Event vs. Display Conversion
- Per-event conversion: each trade, dividend, and fee is converted to CZK using the CNB rate on that specific date. This is what is reported on tax forms.
- Display conversion: portfolio totals shown in CZK on the dashboard use today's rate. This can differ from the sum of per-event conversions.
Tax Rate Selection
Czech tax rules allow using either the daily CNB rate or a single unified yearly rate for each calendar year. Hedger computes both and can automatically select the one that gives lower taxable income.
9. Market Prices
Hedger uses Yahoo Finance as the primary source for daily OHLCV (open, high, low, close, volume) price data. Prices are synced on a schedule to keep historical data current.
Bond Price Estimation
When market price data is unavailable for bonds (common for OTC bonds and some ETFs), Hedger estimates a fair value using the US Treasury yield curve as a reference rate. This is clearly marked as an estimate on the interface.
Price Symbol Resolution
ISIN codes from broker exports are mapped to Yahoo Finance ticker symbols via OpenFIGI. If a mapping is not found automatically, it can be set manually per instrument.
Data Completeness Guarantee
Hedger guarantees that every instrument has a price for every single day in its valid coverage range — no weekend or holiday gaps. This is essential for accurate portfolio value charts, performance calculations, and tax reports.
The coverage range per asset class:
- Stocks / ETFs / indexes: From the earliest date Yahoo Finance has data (or 2010-01-01, whichever is later) until today.
- Bonds: From first known price until the bond matures (or today if not yet matured). Prices are computed daily from the US Treasury yield curve and supplemented by IBKR mark prices.
- Options: From first known price until contract expiry. After expiry, the option is auto-disabled and stops requiring price coverage.
How gaps are filled:
- Real prices come from yfinance (stocks/options) or treasury.gov (bonds) on trading days.
- Weekends, holidays, and any other missing days are forward-filled with the last known closing price (marked as "forward_fill" in the source column).
- FX rates from the Czech National Bank are similarly forward-filled across weekends and holidays.
- Daily portfolio snapshots are then built from this complete price + FX data, one row per day from your first deposit/trade until today.
You can verify the completeness of your own data via the Browse → Prices page, which shows full price history for any instrument and highlights any detected gaps.
10. Corporate Actions
Stock Splits
A stock_split entry carries a split_ratio (e.g., 4.0 for a 4:1 split). All subsequent buy/sell/grant entries for that instrument use quantity × split_ratio to compute the adjusted quantity. The stock_split entry itself is never treated as a trade — counting it would double-count the quantity change.
Mergers
Old shares are closed as a sell-equivalent at the exchange ratio price; new shares are opened as a buy-equivalent at the same adjusted cost basis. The holding period of the original shares carries over.
Spinoffs
The cost basis of the parent shares is split proportionally between the parent and the spun-off entity based on the relative fair market value at the spinoff date. Both lots retain the original acquisition date of the parent for holding period purposes.
ISIN Changes
When a security changes ISIN (common in restructurings and re-listings), Hedger maps the old and new ISIN to the same instrument. Open lots under the old ISIN seamlessly continue under the new ISIN with no artificial close/reopen event.
Exchange Offers & Rights Issues
Treated similarly to mergers: quantity sign determines whether the entry opens or closes a position. Cash components of exchange offers flow through cash balance as cash_settlement.
11. Czech Tax Rules
This section summarises the key rules — full details with worked examples are in the Tax Guide.
3-Year Holding Exemption
Gains from shares held for at least 3 years are exempt from Czech income tax, provided annual proceeds from all securities sales do not exceed CZK 40,000,000 (from 2025). Hedger tracks acquisition dates and flags each pairing as exempt or taxable.
CZK 100,000 Annual Proceeds Threshold
If your total proceeds from securities sales in a calendar year are below CZK 100,000, no filing is required. Hedger computes this total and warns you if you are approaching the threshold.
FX Rate Optimisation
Czech law allows the taxpayer to choose between (a) the CNB rate on the trade date, or (b) a single unified rate announced for the calendar year. Hedger computes taxable income under both methods and can auto-select the lower amount.
EPO XML Export
The tax report can be exported as a signed EPO XML file for direct upload to mojedane.cz (the Czech Tax Administration portal), pre-filled with all relevant lines.
FX currency conversions (§10 F) — opt-in module
Hedger ships an optional module that treats explicit foreign-currency conversions (e.g. selling USD to buy EUR inside your broker account) as separate §10 F realizations. The module is opt-in via Settings and OFF by default — when off, no §10 F FX events are produced and the tax report output matches the effective behavior of mainstream Czech tax tools (including Taxomat).
- Only ledger entries with type
fx_conversiongenerate §10 F events. Stock buys and sells, margin moves, dividends, deposits and withdrawals are NEVER treated as FX realizations — their CZK conversion is already captured per leg inside §10 D stock P&L using daily CNB rates. - FIFO pairing of currency lots is cross-broker by default — currency is fungible across your accounts for §10 purposes. EUR at Degiro and EUR at IBKR sit in the same queue.
- The 3-year time test applies to FX pairings under the movable-property reading of §10(1)(b). Pairings where the matched acquisition is held longer than 3 years are flagged exempt.
- Each event is priced at the CNB rate matching the user's daily-vs-yearly selection on the tax report — switching the toggle reprices the FX pairings consistently with the rest of the report.
- Long-only lot model — there is no short side. A disposal with no matching acquisition is logged as data-quality noise and not surfaced in the UI.
This is one defensible reading of §10 for private-individual currency conversions. Most Czech tax tools default to ignoring private FX (either via the pre-2024 §4(1)(za) exemption that was repealed for 2024, or via the yearly jednotný kurz collapsing within-year FX to zero). Both approaches are defensible; professional consensus on the post-2024 interpretation is still forming. Consult a certified Czech tax advisor before filing with these numbers.
12. Known Limitations & Disclaimer
- Calculations are best-effort based on the data you upload. Incomplete or malformed broker exports will produce incomplete results.
- Market prices are sourced from Yahoo Finance and may be delayed, adjusted, or unavailable for certain instruments (especially OTC, warrants, and delisted securities).
- FX rates are daily CNB fixings — no intraday rates are used. For trades executed within a single day at varying intraday FX rates, the daily rate is an approximation.
- Corporate action handling requires the broker export to include the corresponding entries. If your broker does not report spinoffs or mergers as separate rows, you may need to enter them manually.
- The 3-year holding exemption calculation uses trade dates as reported by the broker. Settlement date vs. trade date differences are not automatically adjusted.
- Hedger is not a tax advisor. Tax legislation changes frequently. Always verify calculated figures with your broker statements and a qualified tax professional before filing.