Documentation
OpenFolio Documentation
Complete reference for every feature, formula, and import format. Use the contents below to jump to any section.
Getting Started
From zero to a working portfolio in minutes
What is OpenFolio?
OpenFolio is an Australian investment portfolio tracker that consolidates stocks, ETFs, crypto, superannuation, dividends, and equity grants into one dashboard. It runs Australian CGT calculations (FIFO/LIFO/MIN_GAINS), tracks franking credits, and projects dividend income — all with your real data.
First steps
Add your first trade
Go to Trades → + Add trade. Enter ticker, market, quantity, price, and date. Or import from your broker CSV.
Import history
Use Trades → Import CSV to bulk-import from MooMoo, CommSec, Stake, Webull, and more.
Record dividends
Go to Dividends → Import CSV and upload your dividend spreadsheet, or record manually.
Set up net worth
Add bank accounts, super balances, and any property under Net Worth.
Review tax
Go to Tax to see realised gains, the 50% CGT discount analysis, and your FY estimate.
Data flow
Trades (BUY/SELL) → holdings-engine → Holdings + Tax Lots
Holdings + Live Prices → bi-engine → Dashboard metrics
Tax Lots + Strategy → tax-engine → CGT summary
Dividends → dividend-engine → Franking credits + Forecast
Net Worth
Your complete financial position in one number
How it's calculated
Portfolio Value + Crypto + Super + Bank + Property − DebtΣ(current_price × quantity) for all holdingsΣ(coingecko_price × quantity) for all cryptoΣ(credit_card_balance) + other liabilitiesCategories
Portfolio
Stock & ETF holdings at current market price
Crypto
Crypto positions valued via CoinGecko live prices
Super
Latest super balance snapshot per fund
Property & Assets
Real estate, vehicles, collectibles — manual entry
Cash & Banking
Bank account balances imported from statement CSVs
Credit Cards
Outstanding balances tracked as liabilities
Portfolio value uses live prices where available. If a price feed is unavailable for a ticker, cost basis is used as a fallback — the holding is still included but may understate market value.
Portfolio & Trades
Cost basis, market value, and unrealised P&L
Cost basis
Every BUY trade creates a tax lot. The cost basis for a holding is the sum of all lot costs for units still held. Brokerage is included in cost basis (it raises your acquisition cost, reducing your eventual taxable gain).
price_aud = price_foreign / aud_rate(price_aud × qty + brokerage) / qtyΣ(quantity × cost_per_unit) over all open lots(current_price × quantity) − cost_basis(P&L / cost_basis) × 100current_price × total_quantityAUD rate for foreign trades
For US stocks, set the AUD/USD rate at time of trade. The field labelled "AUD rate" is units of foreign currency per 1 AUD. Example: if 1 AUD = 0.64 USD, enter 0.64. A $100 USD price becomes $156.25 AUD at that rate.
Portfolio cost-over-time chart
The green stepped line on the Portfolio page shows cumulative cost basis over time — it steps up on each BUY and down on each SELL (by the lot cost, not sale price). The blue line (where available) shows current market value at today's date, giving a visual P&L gap.
Lot Accounting
FIFO, LIFO, and MIN_GAINS — how each BUY and SELL is matched
What is a tax lot?
Every BUY trade creates one lot: a record of quantity, cost_per_unit_aud, and acquisition_date. When you SELL, lots are consumed in a strategy-defined order to calculate your realised gain.
Disposal strategies
FIFOFirst In, First OutOldest lots consumed first. ATO default — lowest audit risk. Maximises CGT discount eligibility when shares have been held long.
LIFOLast In, First OutNewest lots consumed first. Useful when recent purchases had a higher cost — reduces gross gain on short-term sells.
MIN_GAINSMinimum GainsLots sorted by highest cost_per_unit descending — minimises your realised capital gain on any given sell.
Worked example
Suppose you bought VAS in two tranches, then sold 80 units:
FIFO
Consume Lot A first (80 of 100 units) Cost basis: 80 × $90 = $7,200 Proceeds: 80 × $105 = $8,400 Gain: $1,200 (held 878 days → 50% discount ✓)
LIFO
Consume Lot B first (50 units), then 30 from Lot A Cost basis: 50×$95 + 30×$90 = $7,450 Proceeds: $8,400 Gain: $950 (Lot B: 442 days → no discount)
MIN_GAINS
Highest cost first → Lot B ($95), then Lot A Same as LIFO here Gain: $950 (minimised)
The ATO currently mandates FIFO for most individual taxpayers using standard CGT event rules. LIFO and MIN_GAINS are provided for modelling purposes. Consult your tax adviser on which method applies to your situation.
Australian CGT Engine
Capital Gains Tax: 50% discount, loss offsets, FY estimates
CGT event
A CGT event occurs when you dispose of a CGT asset — selling shares, ETFs, or crypto. The gain (or loss) is the difference between your sale proceeds and the asset's cost base.
Formulas
(sell_price − sell_brok/unit) × qty − cost/unit × qtyheld_days >= 365gross_gain × 0.5 (if eligible)non_disc_gains + disc_gains × 0.5 − lossesnet_taxable × 0.39 (39% marginal proxy)How losses work
Capital losses offset capital gains in the same FY. If losses exceed gains, the surplus carries forward to future years (not shown in the current estimate — enter carried-forward losses manually). The 50% discount applies only to gains, not losses — you cannot discount a loss.
Australian financial year
The ATO FY runs 1 July → 30 June. FY2025 = 1 Jul 2024 → 30 Jun 2025. The Tax page groups all realised events by the FY in which the SELL date falls.
The 39% tax rate is a proxy for a high-income marginal rate. Actual tax depends on your total income, offsets, and Medicare levy. This is an estimate only — not tax advice.
Dividends & Franking Credits
Cash income, ATO gross-up, and projected yield
Franking credit formula
Australian companies pay tax at 30% before distributing dividends. If a dividend is "fully franked", the ATO allows you to claim a credit for the tax already paid — the 30/70 gross-up:
cash × (franking_pct / 100) × (30 / 70)cash_dividend + franking_creditcash × (30/70) ≈ cash × 0.4286projected_annual / cost_basis × 100trailing_12m_cash × (365 / days_in_window)Example
Partial franking
ETF distributions often carry partial franking (e.g. 30% or 60%) because the underlying portfolio is a mix of fully-franked, unfranked, and foreign income. The import page lets you set a default; you can edit individual records afterwards.
DRP (Dividend Reinvestment Plan)
Mark a dividend as DRP to flag it as reinvested. DRP dividends still count as assessable income in the year received — you should also add a matching BUY trade at the DRP issue price.
Crypto Holdings
Snapshot-based positions with live CoinGecko prices
How it works
Crypto holdings are stored as snapshots: symbol, platform, quantity, and average cost in AUD. Live prices are fetched from CoinGecko on each page load. P&L is calculated against your average cost.
coingecko_price_aud × quantityvalue − (avg_cost_aud × quantity)(P&L / (avg_cost × qty)) × 100Lot-level CGT tracking for crypto (tracking each individual BUY/SELL event) is on the roadmap. Currently, crypto P&L is based on average cost only — not suitable for ATO reporting of realised gains.
Supported imports
Coinstash and Bybit CSV exports are supported. Binance support is planned.
Superannuation
Balance snapshots and contribution tracking
Balance tracking
Super balances are recorded as point-in-time snapshots — date and balance per fund. The chart on the Super page plots these snapshots with selectable periods (3M / 1Y / 3Y / All). Add a new snapshot each time you check your fund statement.
Contributions
Each contribution can be categorised as:
- Employer SGC — 11.5% (FY2025) of ordinary time earnings, mandatory
- Voluntary concessional — Pre-tax contributions — count toward $30k/yr cap
- Salary sacrifice — Pre-tax, reduces assessable income
- Non-concessional — After-tax personal contributions
- Government co-contribution — Low-income earners: up to $500 government match
- FHSS — First Home Super Saver Scheme withdrawals
SMSF support and retirement projection modelling are on the roadmap.
Equity & ESS
RSUs, options, performance rights, and employee share schemes
Grant types
RSURestricted Stock Unit — shares granted subject to vesting. Taxed as income when vested at FMV.
OptionRight to buy shares at strike price. Gain = (FMV − strike) × qty at exercise.
Performance RightRSU variant — vesting contingent on performance milestones.
ESS DiscountEmployee Share Scheme — shares bought below market price. Discount is assessable income.
Restricted ShareShares issued upfront but legally restricted until vest date.
Vest lifecycle
SCHEDULED → grant awarded, vesting in future
VESTED → vest date passed, shares released
EXERCISED → options exercised (options only)
SOLD → vested shares subsequently sold
LAPSED → unvested grant cancelled (left company etc.)
Net vest value
vest_quantity × current_market_price(current_price − strike_price) × qty (if > 0)market_price_at_grant − grant_priceGEMS import
Export your grant summary from GEMS as CSV. Expected columns:
Importing Data
Supported brokers, bank formats, and step-by-step export guides
CommSec (CDIA Cash Account)
CommSec exports a cash account transaction log. Trades appear as rows with Reference starting 'C'. Bank transfers (Reference 'R') are ignored automatically.
- •Log in → My Portfolio → click your CDIA Cash Account
- •Go to Transactions tab → set date range → Download CSV
- •Brokerage is auto-calculated: |total_debit − qty × price|
- •All trades assumed ASX, AUD
MooMoo
MooMoo exports an Excel file from the FY Summary page. Supports both AUS (ASX) and US trades on separate sheets.
- •Account → Statements → FY Summary → download .xlsx
- •Both sheets (AUS + Wall St) are processed automatically
- •Positions CSV also supported — creates synthetic BUY at avg cost
- •US trades in USD — set AUD rate on import
Stake
Stake exports an Excel file from the Tax & reporting section.
- •Account → Tax & reporting → Financial year reports
- •Download the Investment Activity .xlsx
- •US trades in USD — set AUD rate on import
Webull
Webull exports a CSV from Orders → Trade History. Supports stocks, ETFs, and options.
- •Orders → Trade History → Export → download CSV
- •OPTION type auto-detected — price stored per-contract
- •All trades in USD — set AUD rate
Pepperstone (cTrader)
Pepperstone XLSX has one row per closed position. Each row becomes a BUY + SELL pair at the opening/closing prices.
- •cTrader → History tab → Closed Positions → Export to Excel
- •Each row emits 2 trades: BUY at entry, SELL at close
- •Net AUD stored in notes on the SELL leg
Coinstash
Coinstash exports a transaction CSV from Profile → Transaction History.
- •Profile (top right) → Transaction History → Export
- •Buy/Sell trades auto-detected; rewards and fees filtered
- •All prices in AUD
Dividend Spreadsheet
Your personal dividend record in the standard format. Rows with nil dividend ('$ -') are skipped. Market and franking % are set at import time.
- •Dividends → Import CSV → upload CSV or .xlsx directly
- •Set market (default ASX) and default franking % before importing
- •Ex-date is stored in the notes field for each dividend record
- •Edit individual records to set per-dividend franking % from your tax statement
Bank Statements (CBA / NAB / ANZ / Westpac)
Download your bank transaction CSV from internet banking. Used for Cash & Banking net worth tracking.
- •Each bank has minor date/column format differences — all handled automatically
- •Imported transactions appear in the Cash & Banking section of Net Worth
- •Not used for trade tracking — use the broker-specific importers for that
Generic CSV
Any broker not listed above — use this flexible format. Column names are case-insensitive.
- •Date formats: YYYY-MM-DD and DD/MM/YYYY both accepted
- •Type: Buy/Sell, B/S, Purchase/Sale all accepted
- •Currency: AUD or USD (set conversion rate after upload)
CSV Templates
Copy and fill — or download from the Import page for each broker
Generic Trade CSV
Dividend Spreadsheet
GEMS Grant Summary
Bank — CommBank
Bank — NAB
Bank — ANZ
Bank — Westpac
Formula Reference
Every calculation used in the app, in one place
All amounts in AUD unless stated
price_aud = price_foreign / aud_rate(price_aud × qty + brokerage) / qtyΣ(qty × cost_per_unit) over open lotscurrent_price × total_quantitymarket_value − cost_basis(P&L / cost_basis) × 100(sell_price − sell_brok/unit) × qty − cost/unit × qtyheld_days >= 365 → gain × 0.5non_disc + disc × 0.5 − lossesnet_taxable × 0.39cash × (franking_pct / 100) × (30 / 70)cash + franking_credittrailing_12m / cost_basis × 100|total_debit − qty × price|portfolio + crypto + super + bank + property − debtmax(0, current_price − strike) × qtymarket_price_at_grant − grant_price