← Back to Blog
Product · September 2026 · 9 min read

Altman Z, Piotroski F, Beneish M: Forensic Accounting Scores via MCP

M
Michael · Builder & owner of Stocklake
Altman Z-Score
Bankruptcy-risk composite · Altman, 1968
distress1.812.99safe
Piotroski F-Score
Fundamental strength, 9 binary tests · Piotroski, 2000
0 · weakmoderate9 · strong
Beneish M-Score
Statistical-anomaly screen, 8 ratios · Beneish, 1999
unremarkable−1.78flagged
Three published academic formulas, applied mechanically to each company's own filed statements — not every company clears every formula's own input requirements

Three questions come up constantly when anyone — a person or an AI agent — is handed a company and asked to assess it. Is the balance sheet under strain? Are the fundamentals improving or deteriorating year over year? Does anything in the reported numbers look statistically unusual next to peers and history? Academic finance has had a standard, published, decades-old answer to each of those for a long time. What's usually missing isn't the formula. It's someone actually running it across a whole universe, from each company's own filed statements, and returning the result as a field you can query.

That's what the forensic_scores block is. Stocklake did not invent any of these three formulas, and we haven't tuned or "improved" them. They're implemented as published, computed from the financial statements we already ingest, and returned on get_stock and get_stocks. A score here is a screening input — a reason a filing might be worth reading — never a verdict about a company.

Four terms, before diving in
Altman Z-Score
A five-ratio weighted composite from 1968, built to separate firms under financial strain from healthy ones. The only one of the three that moves with the share price, because one term uses live market cap.
Piotroski F-Score
Nine yes/no tests from 2000, each comparing this year to last year across profitability, leverage and operating efficiency. Summed to a 0-9 integer. Direction of travel, not level.
Beneish M-Score
An eight-ratio linear composite from 1999. Above its threshold means a company's reported numbers share statistical characteristics with a historical sample — not that anything improper occurred.
likely_manipulator
The boolean field carrying the Beneish threshold test. Read it as "this ratio profile is unusual enough to deserve a human look," which is the only thing the underlying research supports.

Three formulas, none of them ours

Altman Z-Score (1968)

The original public-company formula, unchanged: Z = 1.2·X1 + 1.4·X2 + 3.3·X3 + 0.6·X4 + 1.0·X5. The five terms are working capital, retained earnings, operating earnings and sales — each scaled by total assets — plus the market value of equity over total liabilities. Zones are the published ones: above 2.99 is the safe zone, 1.81 to 2.99 is the grey zone, below 1.81 is the distress zone.

The X4 term is why this score behaves differently from the other two. It needs current market cap, so a Z-Score moves when the share price moves, even with the filings unchanged. That's a property of the 1968 model, not an artifact of our implementation, and it's worth knowing if you're comparing a score computed on two different days.

Piotroski F-Score (2000)

Nine binary signals, each worth exactly one point, summed into an integer from 0 to 9. They fall into three groups: profitability (is the company earning, is it generating cash, is return on assets improving, is cash flow exceeding accounting income), leverage and liquidity (is long-term debt falling, is the current ratio improving, was equity issued), and operating efficiency (is gross margin improving, is asset turnover improving). Bands: 8-9 is strong, 3-7 is moderate, 0-2 is weak.

Every one of the nine is a current versus prior comparison, which means the score is structurally about direction of travel. A profitable, stable, slow-growing company can sit at a middling F-Score for years without anything being wrong — it simply isn't improving on most axes, because it doesn't need to.

Beneish M-Score (1999)

Eight ratios, fixed weights, one linear sum. The components are DSRI (days sales in receivables), GMI (gross margin), AQI (asset quality), SGI (sales growth), DEPI (depreciation rate), SGAI (SG&A expense), LVGI (leverage), and TATA (total accruals to total assets). A result above -1.78 raises the flag.

This is the one most easily over-read, so it's worth stating plainly: the model was fit on a historical sample, and what it detects is a ratio profile statistically resembling that sample. It is a screening heuristic. It is not a determination that a company did anything wrong, and it should never be reported as one. The section on limitations below covers four separate, entirely benign business events that trip it.

  Altman Z Piotroski F Beneish M
Published196820001999
QuestionBalance-sheet strainImproving or deterioratingStatistically unusual reporting
Shape5 weighted ratios9 binary tests, summed8 weighted ratios
RangeContinuousInteger 0-9Continuous
Bands>2.99 safe · 1.81-2.99 grey · <1.81 distress8-9 strong · 3-7 moderate · 0-2 weak>−1.78 flagged
Needs prior year?NoYesYes
Needs market cap?YesNoNo

What's actually covered

Not every stock gets all three scores, and that isn't a gap so much as a consequence of what each formula needs to run. Altman needs one current period plus a market cap. Piotroski needs a current and a prior fiscal year, so a company with only one year on file drops out entirely. Beneish needs current and prior figures across all three statements, including depreciation and cash-flow lines that plenty of filers report in a shape the model can't consume. Each requirement is stricter than the last, which is why Altman Z, Piotroski F and Beneish M score progressively fewer companies in that order — Altman is the most widely available of the three simply because it asks the least of a filing, and Beneish the least available because it asks the most.

Within the population that does clear each formula's own requirements, the zones aren't evenly split, and the shape is consistent enough to describe without pinning it to a number that's out of date the moment it's published. Most Altman-scored companies land in the safe or grey zone rather than distress. Piotroski scores cluster heavily in the moderate band — strong and weak are both a minority, which matches how the model is built: it rewards year-over-year improvement, and a stable, unremarkable company can sit in the middle indefinitely without anything being wrong. Beneish's flag is deliberately meant to be uncommon, and it is — only a minority of scored companies clear the threshold at any given time, and the five limitation classes below account for a real share of that minority.

A flag is the start of a question, not a verdict, and coverage itself moves as new statements arrive — querying get_stock/get_stocks directly is the only way to see the current picture rather than a snapshot that's already aged.

All-or-nothing scoring, on purpose

If a single required input is missing, the entire score is withheld. Not estimated, not filled from a peer average, not computed on the fields that happen to be present. Internally the result is a withheld score with a machine-readable reason attached — missing_fields, zero_total_assets, zero_denominator and a couple of siblings.

The reasoning is that a partially-computed Z-Score is not a slightly less accurate Z-Score. It's a different number wearing the same name. Drop the X4 term and you haven't nudged the result — you've removed 0.6 × a ratio that routinely dominates the total for an equity-heavy company, and what comes out will look like a plausible score in the distress range. Nothing downstream can detect that. An explicit null can be detected by anything.

Through the MCP tool, a withheld score surfaces as an explicit null plus a plain-English note explaining why it isn't available, rather than the sub-block silently vanishing from the response. Every sub-block also carries a note describing the formula and its known failure modes, so a score never arrives as a bare number with no context attached:

forensic_scores: { altman_z: { score: <float | null>, zone: "safe" | "grey" | "distress", note: <string> }, piotroski_f: { score: <int 0-9 | null>, strength: "strong" | "moderate" | "weak", note: <string> }, beneish_m: { score: <float | null>, likely_manipulator: <bool>, note: <string> }, computed_at: <ISO 8601 timestamp> }
A withheld score keeps its sub-block and carries an explicit null plus a note — it is never quietly dropped from the response

Five places these formulas mislead

This is the part worth reading closely. All three models were built decades ago against particular kinds of companies, and each has failure modes that are entirely mechanical — the arithmetic works exactly as published, and the output still doesn't mean what a casual reading would suggest. Five classes, all observed in our own live data.

1
Financial-sector companies don't fit at all
Banks and insurers mostly return null on all three. Working capital, retained earnings and "sales" as Altman defines them simply do not describe a bank's balance sheet, where deposits are liabilities and the asset side is loans. Verified on JPM, BAC and WFC — all three return null across all three scores. This is the correct output. A number here would be worse than no number, because it would look usable.
2
Pre-revenue and explosive-growth companies produce extreme Beneish values
Beneish's SGI term is a sales-growth ratio and DSRI is a receivables ratio. A company going from near-zero revenue to meaningful revenue, or scaling several hundred percent in a year, generates ratios far outside the range the model was fit on. The composite can land at an extreme value with no fraud implication whatsoever — the input simply isn't the kind of company the 1999 sample contained.
3
A large capital raise moves LVGI, and LVGI is a Beneish input
LVGI measures the year-over-year change in leverage. A company that raised a substantial amount of equity or issued significant debt in a period will show a genuine, large swing in that ratio. That is arithmetic describing a financing event, not a signal about accounting behaviour — but the composite doesn't distinguish between the two, because it was never designed to.
ObservedGME's flag is explained by exactly this: a real, publicly disclosed change in capital structure flowing through the leverage term. The formula is doing precisely what it was defined to do. What it detected is a financing event.
4
Genuine operational deterioration trips GMI the same way
GMI compares prior-year gross margin to current-year gross margin, on the theory that deteriorating margins create pressure. A margin decline that is fully visible, widely reported, and entirely operational in origin moves that ratio identically to a concealed one. The component cannot tell them apart, and neither can the composite it feeds.
ObservedFord's gross-margin compression is the clean example — a well-documented operating story that raises GMI mechanically. The flag reflects the margin move, not anything about how it was reported.
5
Some balance-sheet structures land in Altman's distress zone by construction
Altman's terms assume a manufacturer-like balance sheet. Two common structures break that assumption without anything being wrong. A REIT is property-heavy and debt-financed by design, which pushes the liabilities-denominated terms hard. A payments or fintech business holding large customer float carries those client balances on its own balance sheet, which distorts working capital and the liabilities base in the same direction.
ObservedKRC (Kilroy Realty, a REIT) and WSE (Wise Group, which holds substantial client float) both land in the distress zone for these structural reasons. Neither reading is a solvency assessment of the company. Note also that Wise is sector-labelled Technology, not Financial Services — so filtering this class of false positive out by sector metadata alone does not work.
The operating rule that falls out of all five: a flag is a question, not an answer. Every one of these formulas compresses a company into a handful of ratios, and compression loses exactly the context that separates a benign cause from a concerning one. The most useful thing a forensic score does is narrow 3,000 companies down to a shortlist worth looking at properly. It cannot do the looking.

How the math was checked

Formula implementations fail quietly. A component computed with its numerator and denominator swapped still produces a plausible-looking number, and unit tests written against synthetic data will happily confirm whatever the implementation does. Beneish is especially exposed here: GMI and DEPI are defined prior over current, while SGAI is current over prior. Get one of those backwards and everything still runs.

So correctness was verified two independent ways, neither of which was a unit test. First, every ratio in all three formulas was hand-derived against real production data for AAPL, MSFT, GME and Ford, and compared against the stored output. Everything matched to three or four decimal places, with zero discrepancies found. That pass required pulling the raw cash-flow statement separately, since DEPI, TATA and the operating-cash-flow inputs aren't derivable from the balance sheet and income statement alone.

Second, a blind cross-check across 25 symbols. A separate AI agent was handed only the raw financial statements — never our computed values, never the expected answers — and asked to derive all three scores itself. Every genuine discrepancy between its results and ours traced back to the checking agent's own arithmetic, including one miscalculated ratio it corrected once challenged and two tallying errors clearly contradicted by its own shown work. Not one traced to a formula bug on our side.

Where to find it

forensic_scores is a Pro field on get_stock and get_stocks, alongside the other computed analytics on those tools. It's recomputed when a company files a new period rather than on a daily cycle, which matches the cadence of the underlying inputs — annual and quarterly statements don't move day to day. The computed_at timestamp on the block tells you when the current values were derived.

One deliberate omission: the underlying per-ratio breakdown is not returned. An eight-item list of unlabelled Beneish component values without the context to interpret them would recreate exactly the "bare number, no meaning" problem the note field exists to solve. The score, its band, and an honest description of what the band does and doesn't mean is the whole contract.

The honest caveat

These are three academic models from 1968, 1999 and 2000, applied mechanically. Accounting standards, disclosure requirements, capital structures and entire business models have changed a great deal since each was published, and Beneish in particular was fit on a sample of companies from a specific era — there is no reason to assume its calibration transfers perfectly to a 2026 software company or a cross-border payments business. Coverage will never be universal: whole sectors are out of scope by construction, and a real share of the universe is missing at least one score at any given moment, for the reasons above. Only a minority of Beneish-scored companies carry the flag at any given time, and the five limitation classes above explain a meaningful share of them.

None of this is investment advice, a prediction, or a statement about any specific company's conduct or solvency. These scores are research inputs. Their genuine value is narrowing a large universe down to a small set worth reading a filing about — and the filing, not the score, is where the answer lives.

Frequently asked questions

Do I need a Pro key to see these scores?

Yes. forensic_scores is a Pro field on get_stock and get_stocks, alongside the other computed analytics on those two tools. Free and guest tiers still get the price, fundamentals and the nine standard technical indicators — just not this block.

Why is the score null for a stock I care about?

Check the sub-block's own note first — it names the reason. Most commonly it's a bank or insurer (Altman's terms don't describe a bank's balance sheet), a company with only one fiscal year on file (Piotroski and Beneish both need a prior year to compare against), or a filer whose depreciation/cash-flow lines aren't reported in a shape Beneish can consume. See "What's actually covered" above for the full breakdown.

Does a Beneish flag mean the company is manipulating its numbers?

No. likely_manipulator means this company's ratio profile statistically resembles the sample the model was fit on in 1999 — nothing more. Read the five limitation classes above before treating any single flag as meaningful; a real capital raise, a documented margin decline, or simply being a REIT can all trip it with no wrongdoing involved.

How often do these scores update?

On each company's own filing cadence, not a fixed daily schedule — annual and quarterly statements don't move day to day, so recomputing more often would just return the same number. The computed_at timestamp on the block tells you exactly when the current values were derived.

Can I get the individual ratios behind a score, not just the final number?

Not through this field, deliberately. An unlabelled list of eight Beneish component ratios with no context to interpret them would recreate the exact "bare number, no meaning" problem the note field exists to solve. What you get is the score, its band, and a plain-English description of what the band does and doesn't mean.