Skip to content

pranaymodukuru/pf-ops

Repository files navigation

personal-finance-ops

A local-first personal finance pipeline powered by Claude Code. Drop a bank statement PDF into statements/ and get structured transaction data, a full interactive dashboard, and spending insights — no cloud service, no API keys, no data leaving your machine.


How it works

statements/
  └── your-statement.pdf
        │
        ▼  /process-statements
        │  Claude reads the PDF, extracts every transaction
        │
        ▼  /clean-data
        │  Dedup, normalise, assign subcategories
        │  → output/processed/cleaned_cumulative.csv
        │
        ▼  python scripts/gen-dashboard-data.py
        │  Converts CSV → dashboard/data/transactions.js
        │
        ▼  open dashboard/Ledger.html
           10-view interactive web dashboard

Claude Code acts as the AI brain: it reads raw PDFs, understands each bank's layout, extracts all transactions, normalises them to a consistent schema, and saves the output. No OCR library, no custom parser per bank — just Claude reading and reasoning.


Features

  • Multi-bank support — N26 (checking + Spaces), American Express PAYBACK, Advanzia Gebührenfrei Mastercard
  • Rich schema — 18 fields per transaction including subcategory, foreign currency, exchange rate, city, payment method, N26 Space sub-account, internal transfer flag
  • Idempotent pipeline — files tracked by SHA256 hash; re-running never double-counts
  • Ledger web dashboard — 10-view static HTML/React app, no server needed
  • Streamlit dashboard — legacy 7-page Python dashboard (still works)
  • Claude Code slash commands/process-statements, /pipeline-status, /reprocess, /clean-data, /start-dashboard
  • Privacy first — all data stays local; .gitignore ensures statements, extracted data, and the dashboard data file are never committed

Setup

Prerequisites: Python 3.11+, Claude Code

git clone https://github.com/pranaymodukuru/personal-finance-ops
cd personal-finance-ops
pip install -e .

No API keys required — Claude Code is the runtime.


Usage

1. Add your statements

Drop any supported bank statement PDF into the appropriate subfolder:

statements/
  ├── N26/
  │   └── n26-october-2024.pdf
  ├── Amex/
  │   └── amex-november-2024.pdf
  └── Advanzia/
      └── advanzia-december-2024.pdf

2. Extract transactions

Inside Claude Code, run:

/process-statements

Claude reads each unprocessed PDF, extracts all transactions, and saves structured output to output/raw/.

3. Clean and enrich

/clean-data

Deduplicates, normalises receivers, flags internal transfers, and assigns subcategories. Writes output/processed/cleaned_cumulative.csv.

4. Start the Ledger dashboard

python scripts/gen-dashboard-data.py --serve

This generates dashboard/data/transactions.js (gitignored — personal data stays local) and opens http://localhost:8765/Ledger.html in your browser.

Note: do not open Ledger.html directly via file://. Chrome blocks loading of local .jsx scripts that way, resulting in a blank page. The --serve flag starts a lightweight HTTP server that avoids this.


Slash Commands

Command Description
/process-statements Find all unprocessed PDFs in statements/ and extract transactions
/pipeline-status Show processed files, transaction counts, and what's pending
/reprocess [file] Force re-extract a specific statement even if already processed
/clean-data Clean and enrich output/raw/cumulative.csvoutput/processed/cleaned_cumulative.csv
/start-dashboard Instructions for opening Ledger or starting Streamlit
/reset Delete all statements, extracted data, and pipeline state (asks for confirmation first)

Ledger Dashboard

A warm editorial-light web app (Monzo / Copilot aesthetic) built with React and inline SVG charts. Opens as a static HTML file — no build step, no server.

View What you see
Dashboard KPI cards (spend, income, savings rate), monthly bar chart, recent transactions, top categories
Net worth & forecast Cumulative balance across all accounts + 6-month projection (dashed)
Transactions Full searchable/filterable table; inline category and subcategory editing
Spending breakdown Donut chart; click any category to drill into subcategories and merchants
Merchants Leaderboard ranked by spend with horizontal bar chart
Subscriptions Auto-detected recurring charges by cadence and amount stability
Budgets & goals Editable monthly envelope budgets + savings goals with progress bars
Income & savings Savings rate trend (interactive line chart), month-by-month breakdown
Investments ETF flow tracking
Travel & FX Foreign currency transactions, spend by currency

Features: privacy mode (blur amounts), dark/warm/cool themes, compact/comfy density, accent colour picker, internal transfer toggle, time range selector (this month → all time), mobile companion view.

Updating after new statements: re-run /process-statements/clean-datapython scripts/gen-dashboard-data.py --serve, then reload the tab.


Transaction Schema

Every transaction is normalised to 18 fields:

Field Type Description
date YYYY-MM-DD Booking/settlement date
transaction_date YYYY-MM-DD Date the purchase occurred (differs on Amex)
receiver string Counterparty name — merchant, person, or institution
reference string Payment reference or note
description string Short clean English description
amount decimal Negative = debit, positive = credit. Always EUR.
currency_original string | null ISO 4217 code if original currency was not EUR
amount_original decimal | null Amount in original currency
exchange_rate decimal | null FX rate used
type debit | credit Direction of the transaction
category string food, transport, utilities, entertainment, healthcare, shopping, income, savings, transfer, fees, travel, learning, insurance, other
subcategory string | null Finer-grained label (e.g. groceries, etf_investments, streaming)
payment_method string | null paypal, sepa_transfer, direct_card, direct_debit, atm
account_type string checking, savings (N26 Spaces), credit_card
space string | null N26 Space name (e.g. "House Rent")
city string | null City/location if stated in document
bank string "N26", "Amex", "Advanzia"
is_internal_transfer boolean True if money moved between own accounts

Project Structure

personal-finance-ops/
├── statements/              # Drop PDFs here — git-ignored
│   ├── N26/
│   ├── Amex/
│   └── Advanzia/
├── output/
│   ├── raw/
│   │   ├── individual/      # Per-statement CSV + JSON — git-ignored
│   │   └── cumulative.csv   # All transactions combined (raw) — git-ignored
│   └── processed/
│       └── cleaned_cumulative.csv  # Cleaned + enriched — git-ignored
├── dashboard/               # Ledger web dashboard (static HTML/React)
│   ├── Ledger.html          # Entry point — open this in your browser
│   ├── styles.css
│   ├── helpers.jsx
│   ├── charts.jsx
│   ├── pages-1.jsx          # Overview, Transactions, Spending, Budgets
│   ├── pages-2.jsx          # Income, Investments, Subscriptions, Merchants, Travel, NetWorth
│   ├── mobile.jsx
│   ├── tweaks-panel.jsx
│   ├── ios-frame.jsx
│   └── data/
│       └── transactions.js  # Generated by scripts/gen-dashboard-data.py — git-ignored
├── scripts/
│   └── gen-dashboard-data.py  # CSV → transactions.js converter
├── src/
│   ├── extractor.py         # PDF → raw text via pdfplumber (fallback)
│   ├── pipeline.py          # Tracks processed files by SHA256 hash
│   ├── save.py              # Writes CSV/JSON and updates pipeline.json
│   ├── cleaner.py           # Cleaning pipeline: dedup, normalise, flag, subcategorise
│   └── subcategory.py       # assign_subcategory() — regex-based classification
├── .claude/
│   └── skills/
│       ├── process-statements/SKILL.md
│       ├── pipeline-status/SKILL.md
│       ├── reprocess/SKILL.md
│       ├── clean-data/SKILL.md
│       └── start-dashboard/SKILL.md
├── dashboard.py             # Streamlit dashboard (legacy, 7 pages)
├── pipeline.json            # Auto-generated; tracks processed files — git-ignored
├── CLAUDE.md                # Instructions for Claude Code
└── pyproject.toml

Supported Banks

Bank Type Notes
N26 Checking account Supports Spaces (sub-accounts), value date vs booking date, sender IBAN
American Express PAYBACK Credit card Two dates per transaction, PayPal merchant detection
Advanzia Gebührenfrei Mastercard Credit card City field, inline FX with exchange rate

Adding a new bank

  1. Drop a statement PDF into statements/
  2. Run /process-statements — Claude identifies the format and extracts accordingly
  3. If the layout is complex, run python -m src.extractor statements/yourfile.pdf to inspect raw text first

Privacy

All processing is local. .gitignore ensures the following are never committed:

  • statements/ — your bank statement PDFs
  • output/raw/individual/ — per-statement extracted files
  • output/raw/cumulative.csv and output/processed/*.csv — transaction history
  • dashboard/data/transactions.js — dashboard data file
  • pipeline.json — file hashes and processing metadata

The repo contains only code, configuration, and documentation.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors