A personal finance tool that ingests bank statements from multiple Australian banks, categorises transactions, and generates ATO-ready tax reports — all from your terminal and a local web dashboard.
- Multi-source ingestion — parse statements from ING, PayPal, Bankwest, HSBC, Coles Mastercard, and Amex (PDF + CSV)
- Auto-categorisation — regex-based rules assign categories to transactions; learns from manual overrides
- Source-of-truth dedup — prevents double-counting when transactions appear on both a bank account and a credit card
- Business splits — automatically allocate percentages of expenses to businesses for tax reporting
- ATO tax return view — structured output matching Australian individual tax return sections (salary, rental, business schedule, deductions)
- Financial year view — outgoing/incoming/rental/work-trip sub-tabs replacing the manual Excel spreadsheet
- Net worth dashboard — accounts, credit cards, property, vehicles with statement-sourced balances
- Tags — orthogonal to categories; sub-classify transactions for reporting (e.g.
flight,biz-hosting,rental-income) - Local-first — SQLite database, no cloud dependency, your data stays on your machine
# Clone and set up
git clone https://github.com/isaacrowntree/ledger.git
cd ledger
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
# Copy and customise config
cp config/accounts.yaml.example config/accounts.yaml
cp config/categories.yaml.example config/categories.yaml
cp config/tax.yaml.example config/tax.yaml
# Edit these files with your accounts, merchant rules, and tax details
# Initialise database
ledger init
# Place statement files in staging/<source>/ and ingest
mkdir -p staging/ing staging/paypal
# Drop your PDF/CSV statements into the appropriate folders
ledger ingest
# Start the dashboard
python -m api
# Open http://localhost:5050| Source | Format | Parser |
|---|---|---|
| ING Australia | PDF statements | etl/parsers/ing_pdf.py |
| ING Australia | CSV export | etl/parsers/ing_csv.py |
| PayPal | CSV activity download | etl/parsers/paypal_csv.py |
| Bankwest | PDF eStatements | etl/parsers/bankwest_pdf.py |
| Bankwest | CSV export | etl/parsers/bankwest_csv.py |
| HSBC | PDF statements | etl/parsers/hsbc_pdf.py |
| Coles Mastercard | PDF statements | etl/parsers/coles_pdf.py |
| Amex | CSV download | etl/parsers/amex_csv.py |
| Airbnb | CSV payout report | etl/parsers/airbnb_csv.py |
Adding a new bank: implement BaseParser in etl/parsers/, add to PARSERS in etl/cli.py.
All config lives in config/ (gitignored — copy from .example files).
Define your bank accounts, their types, and source-of-truth relationships:
accounts:
- name: "My Everyday Account"
source_type: ing
account_type: checking
file_prefix: my_everyday # matches statement filenames
- name: "My Credit Card"
source_type: bankwest
account_type: credit
source_of_truth: true # this account has the real transaction detail
payment_patterns: # patterns on OTHER accounts that are payments to this card
- "MY CREDIT CARD"Categories, regex matching rules, and tag rules:
categories:
- name: Groceries
budget_monthly: 600
rules:
- type: regex
pattern: "WOOLWORTHS|COLES |ALDI"
category: Groceries
tag_rules:
- tag: flight
pattern: "JETSTAR|QANTAS|VIRGIN AUSTRALIA"ATO-specific config — businesses, rental properties, depreciation, WFH:
businesses:
- name: "My Business"
abn: "00000000000"
split_rules:
- category: Utilities
tag: internet
business_pct: 50
rental_properties:
- name: "MY PROPERTY"
ownership_pct: 50
rental_weeks: { 2025: 20 }ledger init # Initialise database and load config
ledger ingest # Ingest all sources from staging/
ledger ingest --source ing # Ingest only ING statements
ledger ingest --dry-run # Preview without writing to DB
ledger split --backfill --fy 2025 # Compute business splits
ledger tax --fy 2025 # Print ATO tax summaryThe Flask API serves data to the dashboard at http://localhost:5050.
| Endpoint | Description |
|---|---|
GET /api/transactions |
All transactions with filters |
GET /api/summary/monthly |
Monthly income vs expenses |
GET /api/summary/category |
Spending by category |
GET /api/spreadsheet/outgoing?fy=2025 |
FY expenses with business splits |
GET /api/spreadsheet/incoming?fy=2025 |
FY income by category |
GET /api/spreadsheet/rental?fy=2025 |
Rental property schedule |
GET /api/ato/return?fy=2025 |
Structured ATO return data |
GET /api/accounts/summary |
Account balances and net worth |
PATCH /api/transactions/<id> |
Update category or notes |
PATCH /api/transactions/<id>/split |
Override business split % |
config/ # Your personal config (gitignored)
accounts.yaml # Bank accounts and source-of-truth rules
categories.yaml # Categories, regex rules, tag rules
tax.yaml # ATO tax config (businesses, rental, depreciation)
etl/ # Extract-Transform-Load pipeline
parsers/ # One parser per bank format (PDF/CSV)
categorizer.py # Regex-based category matching
tagger.py # Multi-tag assignment
splitter.py # Business expense split engine
normalizer.py # Dedup, source-of-truth, insert pipeline
cli.py # CLI entry point
api/ # Flask REST API
server.py # All endpoints
frontend/ # Vite + TypeScript dashboard
src/main.ts # Dashboard, transactions, budget, trends, tax views
src/spreadsheet.ts # Financial year view (outgoing/incoming/rental/trips)
src/api.ts # API client and types
data/ # SQLite database (gitignored)
staging/ # Drop statement files here (gitignored)
tests/ # Tests with generic fixtures
scripts/ # Utility scripts (reingest, etc.)
# Run tests
python -m pytest tests/ -v
# Frontend dev server (hot reload)
cd frontend && npm run dev
# API server (auto-reload)
python -m api- Ingest: Drop bank statement PDFs/CSVs into
staging/<source>/. Runledger ingest. - Parse: Source-specific parsers extract date, description, amount, and balance from each format.
- Normalise: Dedup hashing prevents re-importing. Source-of-truth system marks ING payments to credit cards as transfers.
- Categorise: Regex rules assign a category. Manual overrides in the UI train the learning system.
- Tag: Multiple tags assigned per transaction for sub-classification.
- Split: Business split engine allocates percentages based on category + tag rules.
- View: Dashboard shows spending, income, budgets, trends, and ATO-ready tax reports.
Created by Isaac Rowntree.