Agent architecture Equity research pipeline Claude Code · Python · Excel · Supabase

Six Agents, One Verdict

Six specialised agents research a watchlist, four of them in parallel, and hand a seventh artefact — a scored, sourced, decision-ready workbook — to whoever has to act on it. The interesting engineering is not the research. It is the contract that stops six independent researchers from disagreeing with each other.

6Agents
4Run in parallel
1Shared contract
19Scored parameters
4Red-flag rules

How it runs

One agent defines the rules. Four go and find things. One decides.

watchlist.xlsx ticker · company · state Agent 1 · Framework defines the rules writes framework.json parameters · rubrics · weights · thresholds · red flags · CSP fields every agent reads the same contract Agent 2 · Technical MA · RSI · MACD Bollinger · momentum Agent 3 · Fundamental F-series + Q-series evidence for RF1–RF4 Agent 4 · Sentiment ratings · revisions · news insiders · institutions Agent 5 · CSP IV · liquidity · strike event risk four JSON files, one per agent · values, scores, sources Agent 6 · Decision weight · flag · threshold · write stock_analysis_<date>.xlsx publishes · token-checked Supabase · one row per day → the dashboard reads the newest last run's workbook → previous decision
Agents 2–5 have no dependency on one another, so they run at the same time; agent 6 cannot start until all four have written. The dashed edge is the only thing a run inherits from the run before it — the prior decision per ticker, so a change of mind is visible rather than silent. Then the run goes live, published to Supabase and read by the dashboard the moment you open it.

The contract

framework.json is the single source of truth. No agent negotiates.

Agent 1 does no research. Its whole job is to write down what the other five will be held to: which parameters exist, the 0–10 rubric for each one — specific enough that two researchers score the same raw value identically — the category weights, the decision thresholds, the four red-flag definitions and which parameter each derives from, and the CSP field list copied verbatim so agent 5 has nothing to interpret.

The payoff is that weights and thresholds are configuration, not code. Changing Quality & Moat from 40% to 30% is an edit to one JSON file; no agent logic moves, and the next run reprices every name consistently. It also makes the mandatory parameters enforceable: F4 and F5 must exist because RF2 and RF1 derive from them, and Q2 must exist because RF3 does. Agent 1 validates that before it finishes, along with the weights summing to 1.0.

CategoryWeightCarries
Quality & Moat40%Moat durability, 5-year ROIC consistency, capital allocation, margin of safety. The largest weight, deliberately — it is the part that is hardest to fake and slowest to change.
Fundamental25%Valuation against sector, revenue and EPS growth, debt-to-equity, free cash flow trend.
Technical20%Moving averages, RSI, MACD, Bollinger position, 1- and 3-month momentum.
Sentiment15%Analyst consensus and revisions, 30-day news, insider activity, institutional ownership change. The smallest weight — it is the noisiest input.

The override

Some findings must not be averaged away by a good score somewhere else.

A weighted average is a machine for hiding a single fatal fact under nineteen ordinary ones. The pipeline borrows Munger's inversion — avoid stupidity before seeking brilliance — and gives four findings the power to overrule the arithmetic that produced the score.

FlagTriggers onDerived from
RF1Negative or deteriorating free cash flowF5 — FCF trend, 3+ years
RF2Debt-to-equity above threshold (default 2.0, sector-adjustable)F4 — debt-to-equity
RF3Declining multi-year ROIC trendQ2 — 5-year ROIC series
RF4Governance and accounting concerns — restatements, auditor changes, SEC actions, aggressive revenue recognition, heavy dilution, related-party structureDirect research, informed by Q5
SCORE SAYS AFTER THE FLAG BUY any confirmed flag → downgrade HOLD HOLD unchanged HOLD SELL unchanged SELL NO ACTION unchanged NO ACTION
One transition fires and no other. The override can only make a decision more conservative — it never upgrades, and it never manufactures a SELL out of a HOLD. That asymmetry is the whole point: a red flag is a reason not to buy, not evidence of a reason to sell.

Flags are three-state, not boolean

A flag whose source parameter could not be assessed is reported triggered: false with status: "uncertain" — never as a clean pass. An uncertain flag does not trigger the override, and it does not get to masquerade as a green light either. It is surfaced and left unresolved, which is the honest state.

Every flag is cross-checked against its source

RF1 must agree with that ticker's F5, RF2 with F4, RF3 with Q2. A flag that contradicts the parameter it derives from is treated as a merge bug to be fixed before the workbook is written, not shipped with a note. An override with no named trigger is a defect by definition.

Missing data outranks the score

If any single category has two or more unresolved parameters, the decision becomes NO ACTION regardless of what the arithmetic produced — and regardless of the override, since the problem is absent evidence rather than business quality.

Why the options verdict is its own agent

The restructuring that mattered most.

"Is this a good business to own?" and "is this a good stock to sell puts on?" are different questions with different answers. A high-quality compounder trading at a premium with placid options is a fine business and a poor put. A mediocre business with rich implied volatility is a tempting put and a bad thing to own. Averaging them into one number destroys both signals.

So the cash-secured-put verdict is computed by its own agent and carried to the workbook untouched — adjacent to the decision, never blended into it. It is allowed to read the red flags, because the downside of selling a put is being made to own the stock, and a rich premium on a business with a triggered flag skews toward Skip rather than Good. The influence runs one way only.

LANE 1 — IS IT A GOOD BUSINESS? 19 scores weighted score red-flag gate Decision a triggered flag informs the CSP rationale only LANE 2 — IS IT A GOOD PUT? IV · liquidity strike · DTE event window CSP verdict never merged
Two verdicts reach the workbook side by side and neither is allowed to move the other. The only edge between the lanes is the dashed one: a triggered red flag is written into the CSP rationale, because being assigned a flagged business at the strike is the actual downside being priced.

It took three cuts

Each one answered the question the last one raised.

First cut

Does it work?

Five agents, three categories, one spreadsheet. It worked. Then I looked closer.

Second cut

Can I trust it?

Moat gets the biggest vote. Options get their own lane. Red flags get a veto, but only downward. Every number brings its receipt.

Third cut

Does it run without me?

Every weekday at the close, it does its homework and posts the results. Nobody presses a button.

What you just read is the third cut, not the first. The first was five agents, and it worked: three categories, a threshold, a workbook. It just couldn't tell a durable business from a cheap one, or a business worth owning from a stock worth selling puts on. The second cut fixed that with the three changes below. Each one removes an average that was hiding something.

Scroll to compare →

FIRST BUILD — FIVE AGENTS AGENT 1 Framework framework.json AGENT 2 Technical MA · RSI · beta AGENT 3 Fundamental F-series only AGENT 4 Sentiment ratings · news three JSON files AGENT 5 Decision three categories stock decision only re-cut SECOND CUT — SIX AGENTS AGENT 1 Framework framework.json AGENT 2 Technical MA · RSI · beta AGENT 3 Fundamental + Quality & Moat 1 AGENT 4 Sentiment ratings · news AGENT 5 CSP unscored verdict 2 four JSON files AGENT 6 Decision four categories 3 decision + CSP verdict
The shape is unchanged: one agent writes the contract, a band of researchers reads it in parallel, one agent merges. What moved is inside that shape — a fourth scored category, a fifth researcher that is deliberately not scored, and a merge step that slid one position down because a lane was added ahead of it.
1

Quality & Moat became a category of its own

Then: three scored categories
Now: four — Quality & Moat at 40%

The first framework scored Technical, Fundamental and Sentiment. Fundamental meant valuation against sector, revenue and earnings growth, debt-to-equity and free-cash-flow trend — recent-period arithmetic, all of it. Nothing in the parameter set asked whether the business could defend its returns.

Why it changedThose figures describe a company's last few quarters, not its ability to keep them. Scored alone, they rank a cheap, fast-growing business above a durable one, and the ranking flips as soon as a quarter turns. Moat durability, five-year ROIC consistency, capital allocation and governance were added as a Q-series and given the largest weight — 40%, more than any other category — so the output is most sensitive to the evidence that moves slowest and is hardest to fake. The weight is a value in framework.json, not a constant in code, so the split cost one edit to the contract. It also made the override possible: RF3 reads a five-year ROIC trend and RF4 leans on a governance baseline, and neither of those parameters existed before.

2

The CSP verdict was lifted into its own parallel agent

Then: no options lane
Now: agent 5 — parallel, unscored

The first build answered one question: is this a good business to own? Adding options analysis invited the cheap move — score implied volatility and liquidity as a few more parameters and let the weighted average absorb them.

Why it changedThat would have answered neither question. A high-quality compounder with placid options is a fine business and a poor put; a mediocre business with rich implied volatility is a tempting put and a bad thing to own. A single number cannot carry both, and blending them destroys the two signals it is made from. So the put verdict is computed by its own agent, in parallel with the researchers, and reaches the workbook as Good / Neutral / Skip — adjacent to the decision, never inside it. Exactly one edge crosses between the lanes, and it runs one way: a triggered red flag is written into the CSP rationale, because being assigned a flagged business at the strike is the downside actually being priced.

3

The decision agent moved to position six

Then: agent 5, waiting on three
Now: agent 6, waiting on four

The decision agent is the only step in the pipeline that is not parallel. It cannot start until every researcher has written, because its job is to weight, gate and threshold a complete set of files. Adding CSP to the parallel band put a fourth writer ahead of it and pushed it down a slot.

Why it changedThe renumbering is not the change — it is the evidence that the change was cheap. The merge step reads finished JSON and the contract, and nothing else: its thresholds and weights were already values it looked up rather than constants it carried. So a new category and a whole new lane arrived without the merge logic being rewritten to accommodate either. Position six is a consequence of the dependency graph, not a decision about ordering — and the fact that it could move by renaming a file is the property worth protecting as the pipeline grows.

The workbook leaves the building

Same scoring. New address.

The second cut was clever and invisible: a workbook on one laptop, only as fresh as the last time I opened it. Now, every weekday after the market closes, the pipeline reruns and checks its own math. The workbook has to pass a formula check with zero errors before anything is published. Then the run goes to a Supabase database, and the dashboard reads the newest one the moment you open it.

Anyone can read. Only the pipeline can write.

The page's key is read-only. Writing goes through one database function that checks a secret token first and turns away any run with no tickers in it. The token lives on my machine, not in this site.

It has a plan B

If the database takes more than eight seconds, or sends back a run with missing scores, the page quietly shows a saved copy instead. A half-baked run never makes it to the screen.

Zero deploys per run

A new run doesn't touch a single website file. The code changed once, to read from the database. The data changes every weekday.

The rules that keep it honest

Most of the design effort went here, not into the research.

Null beats a guess

A value that cannot be found or verified is recorded as null with a reason. No agent is permitted to fabricate a plausible number, and a category with too many nulls escalates to NO ACTION rather than producing a confident average of the parts that happened to resolve.

Every value carries a source and a date

Not a bibliography per stock — a URL and an as-of date on each individual reading, so any single number can be traced to the page it came from and aged appropriately.

Rejected evidence is logged, not silently dropped

IV Rank is defined against a trailing 52-week range, so it is meaningless for a stock listed less than 252 trading days ago. If a data provider publishes one anyway, the agent records that it rejected the figure and substitutes absolute IV, peer-relative IV, and IV against 30-day realised volatility instead.

Options data is assumed hostile

Implied volatility and greeks are the easiest thing in this pipeline to hallucinate plausibly, so agent 5 may only report retrieved figures with a source and timestamp. Premium yields are labelled indicative rather than executable, and a single premium is never annualised and presented as an expected return.

The workbook computes, it does not store

Scores, weighted totals and decisions are written as live Excel formulas rather than baked values, then validated with a recalculation pass that must return zero errors — plus a manual spot-check of a few rows, because a recalculation check will happily confirm a formula that points at the wrong column.

Every run is kept

Each run gets a dated workbook and its own row in the database, one per day. Each run also reads the last one to carry forward the previous verdict. When the system changes its mind, you see the change, not a brand-new opinion.