One position, tracked properly, scored the same way every time
A decision-support scorecard for a single stock position — weighted evidence, deterministic scoring, never a trade.
- TypeScript
- Supabase
- Claude API
Where this could work for other businesses
The obvious extension is any single, recurring, high-stakes decision that isn’t worth a full portfolio-scale system but still deserves more rigour than gut feel — a board holding one significant vendor relationship, a fund with a concentrated position in one name, a family business watching one supplier it can’t easily replace.
The less obvious reuse is anywhere a decision needs to be defensible after the fact, not just fast in the moment. A board considering a major vendor relies on the same shape: named criteria, weighted by what actually matters to that decision, evidence tested against a bar before it counts, and a paper trail showing why the call was made, not just what the call was. Credit and underwriting decisions already have this structure, formalised over decades — this is that same discipline, sized for a single ongoing decision instead of a one-off approval.
The problem
Following one stock properly, over months, against a specific thesis, meant asking myself the same question every time something happened: does this actually change anything, or is it noise I’d forget about by the next earnings call? Without a structured record, that judgement call got made from memory, under time pressure, right when a headline landed — exactly the conditions under which memory is worst and pressure is highest.
This is a different problem from the always-on investment analyst elsewhere on this site, which scans 230+ tickers continuously and gates a broad watchlist. This one goes the other way: one position, tracked in real depth, reviewed on a deliberate cadence rather than every half hour.
The goal
Build something that never places a trade, never claims certainty, and never lets the same piece of evidence move the thesis twice — a structured, repeatable answer to “has anything actually changed,” not a signal to act on blindly.
What it does
Evidence reaches the scoring engine two ways: AI reads new company news and proposes a classified entry — direction, materiality, source tier, its reasoning — but nothing is scored until I explicitly approve or reject it; and anything else, a position change, an earnings result, a note worth capturing, gets entered by hand, with the same discipline applied either way. The news scan doesn’t stop at the company’s own headlines, either — several of the categories this tool tracks are really about what the big cloud players do with their infrastructure spend, not what the company itself announces, so the scan reads their news directly rather than only anything already tagged under the one ticker’s own name.
Before anything gets scored, it has to clear an eligibility filter — not already scored, materiality at medium or higher, sourced from a tier reliable enough to act on, dated on or after the last earnings report, and either genuinely new since the last report or explicitly marked as reinterpreting evidence that came before it. That combination is what stops the same piece of news from quietly moving the thesis twice.
What survives the filter is scored across six weighted categories, tagged against sixteen specific thesis indicators so a broad category label is never enough on its own. The company’s own signals carry the most weight, followed by the sector’s revenue path and the hyperscaler capex driving it; the lighter end of the weighting covers narrower, more specific factors. The result rolls up into bull, base, and bear scenario probabilities, checked against the current price and position size, and lands on one action status: add, hold, trim, wait, a risk review, or, honestly, data insufficient, when the inputs aren’t there yet to say anything at all.
The build
Built with Claude Code, with one rule underneath everything else: the model that classifies a piece of news doesn’t score it, and it never sets the action status either. Claude’s job is narrow and sits only at the front of the pipeline — read an article, propose a classification, explain why. The scoring engine, the eligibility rules, and the action-status logic are pure functions underneath that, typed inputs in, a typed result out, no side effects, no knowledge of where the data came from — the same evidence and market state always produce the same score, which isn’t something you can say about asking a model to “assess the thesis” fresh each time and hoping the reasoning holds steady.
One judgement call lives inside the classification step on purpose: news dated before the last earnings release is presumed already priced in and capped at low materiality, unless it’s itself later commentary reinterpreting something — the same discipline a careful analyst applies by habit, made explicit instead of left to chance. The market data and news scan run automatically each day, and the scheduled path is deliberately profile-aware: it runs once per active account rather than assuming there’s only ever one, so unattended processing stays correctly isolated the same way interactive use already is.
Problems & solutions
Symptom: the automated news scan, scoped only to the company’s own name, was structurally blind to a whole category of evidence the model itself was built to weigh. Diagnosis: several of the thesis categories are explicitly about what the hyperscalers spend on infrastructure, not what the company itself says — and none of that reliably shows up tagged under the company’s own name. Fix: widen the scan to read the actual hyperscalers’ own news directly, alongside the company’s. The same lookback window went from roughly 150 articles to over 800 once the additional sources were included, and real, on-thesis evidence started reaching the review queue for the first time.
Symptom: an automated, unattended run of the pipeline could resolve to the wrong account once more than one existed, silently feeding evidence and scores to a profile nobody was actually reviewing. Diagnosis: an interactive request carries a logged-in session; a scheduled trigger doesn’t, and a fallback built for “there’s only ever one account” breaks quietly the moment that stops being true. Fix: make the unattended path explicitly account-aware rather than assuming a single implicit owner — a structural fix, not a patch, and a reminder that a scheduled trigger is a genuinely different code path from an interactive one and needs its own test.
Symptom: a category could be seeded with a real weight but no indicator underneath it that could actually earn it, quietly capping the maximum possible score short of the full range without anyone deciding that on purpose. Diagnosis: weights and indicator coverage were designed separately and never checked against each other. Fix: verify every weighted category maps to at least one real indicator, and keep that check in place rather than trusting it was right once.
Symptom: matching a piece of approved evidence back to the specific thesis indicator it affected was failing silently, every time. Diagnosis: the match was against a full descriptive sentence the model had to reproduce exactly — any paraphrase broke it. Fix: match on a short, stable code instead of prose the model has to get word-for-word right. Fixed the linking completely on the first real attempt afterward.
The stack
| Component | Why |
|---|---|
| Vite + TypeScript, no framework | A deliberately small frontend; the scoring logic, not the UI, is where the complexity belongs |
| Supabase (Postgres + Row-Level Security) | Auth and data storage, isolated per account, with scheduled functions calling directly into it |
| Claude API | Reads news and proposes a classification — direction, materiality, source tier — and nothing more |
Results
The first genuinely automated run proved the point immediately. The widened evidence net picked up fresh, real hyperscaler capex news, classified it for review, and after approval produced a small, real positive move in the score — not a dramatic call, and it was never meant to be one. It was a repeatable answer to “has anything actually changed?”, with the underlying evidence, the eligibility decision, the category impact, and the resulting action status all sitting there to check against, instead of a mental re-derivation done from memory under pressure.
What’s next
Analyst price-target and EPS-estimate tracking is currently manual — the free-tier data provider brought in for it stopped reliably covering the position partway through the build, a real dependency risk of leaning on a free external API for anything that actually matters. A paid provider, or a different free one, is the obvious next piece of work. Beyond that, the same before-and-after this tool just proved for one stock is worth testing against a second, genuinely different thesis, to see whether the category weights and indicators generalise or need to be rebuilt each time.