Stocks Tracker
A small webapp that gives me a single view of my positions across brokerages, with drift against target allocation surfaced at a glance.
- holding now target drift
- Alpha 32% 30% +2.0pp
- Beta 18% 20% −2.0pp
- Gamma 14% 15% −1.0pp
- Delta 12% 10% +2.0pp
- Epsilon 14% 15% −1.0pp
- Cash 10% 10% 0.0pp
Under the hood — for the technical folks
Claude built this as a FastAPI backend talking to a brokerage-aggregator API in read-only mode, with a React + Vite frontend that renders the holdings grouped by ticker and the drift against a target allocation. The backend caches the brokerage responses in SQLite so the page is fast even when an upstream account is slow, and a ticker-equivalence layer sits between fetch and render so the same underlying instrument under different wrappers (Class A versus Class B, an ETF mirror, share-class differences inside the same fund) groups into one row instead of three.
The webapp is localhost-only by design — nothing about a personal-finance tool needs to be reachable on the open internet. The aggregator integration uses a read-only connection that has no permission to place orders, transfer funds, or change account state; it can only see balances and positions. Targets and equivalence rules live in a config file outside the database so a tweak doesn’t require a migration, and the cache has a freshness timestamp visible on the page so a stale balance never reads as live.
Technical highlights Claude built under direction:
- A read-only brokerage-aggregator integration scoped to balance + position data only — no order placement, no fund transfer, no account modification can happen through this app
- A ticker-equivalence layer between the raw brokerage response and the render layer, so the same underlying instrument under different wrappers groups into one row
- SQLite cache of brokerage responses with a freshness timestamp visible on the page, so a stale balance never reads as live
- A target-allocation config and a drift calculator that flags any holding more than a small threshold off target
- Rebalance suggestions sized to a stable chunk so the tool doesn’t ask for a different trade every time prices wiggle
- Cross-platform dev parity — the backend and frontend run the same way on Windows and macOS, with a Playwright suite plus a Windows CI runner so platform-specific quirks (Windows path separators, line endings, console encoding) get caught before they ship
Stack: FastAPI · React · Vite · TypeScript · SQLite · Pydantic · Playwright · Windows CI
All mockup data on this page is invented — no real positions, no real brokerages, no real symbols, no real dollar values. The Greek-letter labels on the chart are placeholders.
What I wanted
I wanted one view of where my money actually is. The only way to see a real picture of my allocation was to open each brokerage’s web app, eyeball the percentages, and try to do the cross-account math in my head — which I never trusted because the math kept changing every time I added another wrapper class or another account. I wanted a webapp on my home network that did the math for me: pull the live balances over a read-only connection, group them by the actual underlying instrument across accounts, and tell me how far my real allocation has drifted from the targets I’d set.
How I got it
I told Claude to build a small FastAPI service that talks to a brokerage-aggregator API in read-only mode, plus a React frontend that renders the holdings grouped by ticker and the drift against a target allocation I keep in a config file. The first version just rendered a flat table. The second version added the donut and the per-ticker target column. The third version added rebalance suggestions — given my targets and my drift, what to add to and what to trim, on a chunk size stable enough that I’m not playing whack-a-mole every week.
The harder parts weren’t the visuals, they were the data shape. Holdings can sit in multiple accounts under different ticker classes for the same underlying — Class A versus Class B, an ETF that wraps the same thing twice over — and a naive grouping gets the percentages wrong. I told Claude to keep the brokerage data raw, treat ticker equivalence as its own layer, and let me tune the rules in a config file without touching the fetch code. That separation is what let me extend the tool without re-debugging the fetch every time I added an account.
What it does
It logs into the brokerage aggregator using a read-only token, pulls every position across the accounts I’ve connected, normalizes the tickers that are the same underlying instrument under different wrappers, and renders the whole picture as a donut plus a drift table. The donut shows where my allocation actually is; the table shows where it should be and how much each holding is over or under. Anything that’s drifted more than a couple of percentage points from target flags itself. The webapp runs locally on my home network — nothing leaves the house — and the brokerage connection only ever reads.
Why it matters to me
Money decisions are the easiest place to lose hours to spreadsheet math nobody actually trusts. Before this, the rebalancing ‘process’ was opening four browser tabs, copying numbers into a spreadsheet that always drifted, and never being totally sure the picture I was looking at was right. The tracker doesn’t replace any of the judgment — I still pick the targets, I still decide what to trim — but it gets the math out of my head and onto a page I can actually look at. The point wasn’t to build a demo of a tracker. It was to build a tool I’d rely on with my own positions, which is also why everything sensitive about it is invisible from the outside.
What I learned
The lesson that kept repeating itself was that data normalization is the whole game when you’re aggregating from different systems. The donut and the table were a one-day job; the ticker-equivalence layer that makes them honest took most of the work. Every time I added another account or another wrapper class, the equivalence rules needed to grow, and the shape that finally worked — tune the rules in a config file without touching the fetch code — only emerged because I told Claude to keep the layers separate from the start. The takeaway I’d underline for any aggregation tool is that the part that earns trust is the cleaning layer, not the visualization sitting on top of it.
Trust in a tool I'd point at my own money turned out to be mostly about what I kept out — read-only access, no public surface, nothing on the page I wouldn't show over my shoulder. The rest of the design has to live inside those rails.