WealthFolio — Self-Hosted Portfolio Tracker
Production-grade Indian investment portfolio tracker built with Go and React 19. Multi-broker import (Zerodha, Groww, INDMoney), Gmail auto-import, FIFO cost basis, XIRR, TimescaleDB time-series snapshots, and AI market analysis — deployed as a single Docker binary on a Raspberry Pi.
WealthFolio — Self-Hosted Portfolio Tracker
Every Indian investor tracking multiple brokers ends up with the same problem: Zerodha, Groww, and INDMoney each have their own app, their own data format, and no unified view. Third-party aggregators exist but require handing over your brokerage credentials. This is the self-hosted alternative — all data stays on your own hardware, and the broker integrations work from CSV/XLSX exports and email parsing.
Stack
Backend: Go with chi router, pgx for raw SQL (no ORM), goose migrations, gocron for background jobs, Server-Sent Events for real-time progress.
Frontend: React 19 + Vite 6, TanStack Router (file-based routing), TanStack Query, Tailwind CSS, shadcn/ui, Recharts.
Database: TimescaleDB (PostgreSQL + hypertable extensions) for daily portfolio snapshots with continuous aggregates for fast monthly rollup queries.
Deployment: Multi-stage Docker build — Node 22 builds the React SPA, Go embeds it as a static filesystem, distroless runtime. Final image is ~3MB. Runs as a single binary on a Raspberry Pi.
What It Does
Multi-broker import — Auto-detects file format (Zerodha CSV tradebook, Groww MF XLSX, INDMoney XLS) and normalizes to a unified transaction schema. Duplicate detection uses order ID matching + a heuristic score (date, qty, price) to handle basket orders that share a date but have different IDs.
Gmail OAuth auto-import — Watches inbox for transaction emails from all three brokers. Zerodha sends encrypted PDFs; the parser handles them. Refresh token stored in the app settings table, not env vars.
FIFO cost basis + XIRR — Replays all transactions chronologically to compute true tax-lot cost basis per holding. XIRR uses a Newton-Raphson solver in Go — cash flows are buy amounts (negative), sell proceeds (positive), and current position value.
TimescaleDB snapshots — Daily portfolio value snapshots stored in a hypertable with 30-day compression. Includes breakdown by asset type, platform, and custom category. Continuous aggregates power the monthly returns chart without re-scanning raw data.
Market intelligence — P/E ratio analysis with mood classification (Green/Yellow/Red bands), Tickertape Market Mood Index gauge, gold/silver price tracking, and AI-powered market analysis via configurable provider (Anthropic, OpenAI).
Distribution calculator — Given a target allocation % per instrument/category and a new investment amount, calculates exactly where to deploy the money to rebalance toward targets.
Key Design Decisions
Running on a Raspberry Pi made constraints concrete: the Go binary with embedded React SPA means no separate static file server, no Node runtime in production. TimescaleDB’s compression handles the daily snapshot volume without the storage blowup you’d get from a naive append-only table.
No ORM — all queries are raw pgx. With 23 migrations and a well-defined schema, the overhead of an ORM adds more complexity than it removes for a single-developer project.
The frontend build embeds into the Go binary at compile time via //go:embed. One container, one port, one binary to deploy.
Features
- 11 pages: dashboard, holdings, transactions, allocations, trends, market mood, closed positions, backfill, categories, AI signal, settings
- Privacy mode: one-click sidebar toggle to mask all rupee amounts
- PWA: installable on iOS/Android with offline capability via service worker
- API keys for external integrations
- 50+ REST endpoints across portfolio, auth, markets, transactions, and analytics