Valuation sanity check
Analyst workflows as Claude skills. 62+ tools and 8 workflows spanning earnings, comps, valuation, options flow, factor research, sizing, risk, TCA, and ops. Built in the garage, not the trading floor.
npx -y skills add rgourley/quant-garage --skill valuation-sanity-checkAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Sanity-check an internal analyst valuation thesis against the live peer set. Input the target price, assumed revenue growth, assumed EBITDA margin, and horizon; the skill pulls the current name, builds the peer cohort, computes target-implied multiples vs the peer 25-75 band, compares the growth and margin assumptions to the peer distribution, runs a simplified reverse-DCF, and emits either a single-point fair-value estimate or a full fair-value distribution (--mc flag) as a one-page sell-side flash note answering "is this target defensible or has the model drifted from reality." Use when a banker, PM, or analyst is stress-testing a price target or pitch-deck valuation. Requires Stocks Starter.
SKILL.md
10.8 KB, as published. Nobody here has run it
valuation-sanity-check
You hand over a subject ticker and the thesis: target_price,
assumed_growth, assumed_margin, horizon_years. The skill pulls
the current price, market cap, balance sheet, and TTM financials,
builds the peer set using the same waterfall as pitch-comps
(curated override → correlation → SIC fallback), pulls peer multiples
and growth/margin metrics, and emits a one-page flash note covering
four sanity checks.
This is the "is the model defensible or has it drifted" workflow. The take at the top says whether the target survives the peer-distribution sanity check; the four supporting sections show where the air is.
When to invoke
- A banker is stress-testing an MD's pitch-deck target price
- A PM is reading a sell-side note that says "$250 target" and wants to know what's already baked into the current price
- A junior analyst handed off a model and you need to figure out whether the assumptions are defensible vs the peer set
- The user says "sanity-check $TICKER target $X", "is $X realistic for $TICKER", "what growth does the current price assume"
What you need
- A subject ticker (NVDA, CRM, etc.)
- The analyst's thesis:
target_price(USD/share),assumed_growth(decimal, e.g. 0.28 for 28%),assumed_margin(decimal, e.g. 0.60 for 60%),horizon_years(integer, default 5) MASSIVE_API_KEYexported in the environment- Stocks Starter plan minimum. The full peer fanout is the same
~9 ticker-details + ~9 financials calls as
pitch-comps; under 30 seconds on Starter, ~5 min on free Basic.
What you get back
Two output layers from one analysis.
Layer 1: canonical JSON matching output-schema.json.
Subject metadata, analyst inputs, three sanity-check blocks
(multiple_sanity[], growth_sanity, margin_sanity), the
reverse_dcf block, peer list with each peer's contributing data,
the bold take, the closing read, and per-call source endpoints with
fetched-at timestamps.
Layer 2: rendered note in sell-side flash-note style, modeled on
earnings-drilldown note mode. See references/rendering.md.
Bold take at the top, three sanity sections, reverse-DCF block,
closing read.
How it works
- Pull the subject's live state. Snapshot for current price (via
the standard
lastTrade → day.c → prevDay.c → fmvwaterfall), ticker details for shares outstanding and market cap, financials for TTM revenue, operating income, D&A, balance sheet (long-term debt). Same data layer aspitch-comps. - Compute target-implied financials.
target_mcap = target_price × shares_outstanding.target_EV = target_mcap + long_term_debt(cash not subtracted; documented inreferences/multiple-sanity.md).target_revenue_horizon = subject_revenue_ttm × (1 + assumed_growth)^horizon.target_ebitda_horizon = target_revenue_horizon × assumed_margin.target_eps_horizonderived from the operating-margin-implied net income. - Build the peer set per
references/peer-selection.md. Reuses the pitch-comps three-layer waterfall and the shared override map. - Pull peer multiples and metrics. Same per-peer fanout as
pitch-comps: current price, market cap, TTM financials. Compute each peer's EV/Sales, EV/EBITDA, P/E, revenue growth TTM, EBITDA margin. - Multiple sanity per
references/multiple-sanity.md. For each multiple (EV/Sales, EV/EBITDA, P/E), compute the target-implied value at the horizon and compare to the peer 25-75 percentile band. Status:in_line(inside band),above(above p75),below(below p25). - Growth and margin sanity per
references/growth-margin-sanity.md. Compare the analyst'sassumed_growthandassumed_marginto the peer 25-75 bands on revenue growth and EBITDA margin. Same status labels. Records thedelta_pp(assumed minus peer median, in percentage points) so the reader can quote the gap. - Reverse-DCF per
references/reverse-dcf.md. At the current stock price, given the assumed margin and the peer-median EV/EBITDA exit multiple, what 5-year revenue CAGR is implied? Compare to peer-median 5y CAGR (proxied from TTM growth when 5y history is missing). Surfaces "air in the current price": the gap between the implied CAGR and the peer-median CAGR. - Generate the take and the read per
references/take-generator.md. Bold take at the top in one paragraph: the CAGR/margin the target requires and how far it sits from peer median. Closing read at the bottom: if you trim assumptions to peer median, where does the target land. Banker-tone, no hedge words.
Foundations used
massive-api-patternsfor REST auth, rate limiting, the snapshot fallback chain, and the financials-endpoint null-handling.
Output mode: note
Same note mode as earnings-drilldown. Bold take at top, grouped
supporting sections, closing read, one-page max. The format follows
the sell-side flash-note convention. See references/rendering.md
for the per-section rules.
A custom UI consumes the JSON and renders the three sanity sections as side-by-side comparison bars (assumption vs peer band) with a scatter inset for the reverse-DCF view. Claude Code users read the rendered note.
MC mode
Pass --mc when the single-point fair value reads as implausibly
precise; emits a sampled distribution + sensitivity ranking instead.
Drivers (growth, margin, exit multiple) come from the same peer set
the point-estimate path uses. See references/monte-carlo.md
for the methodology, sampling defaults, and what MC mode does NOT do
(it is not a forecast; it is a sensitivity sweep around peer-derived
inputs).
Flags:
--mc— enable Monte Carlo fair-value distribution (default off).--mc-samples N— sample count, clamped to[1000, 100000], default10000.--mc-distribution {peer,normal}—peerresamples from the peer empirical distribution (default);normalfits N(mu, sigma) to the peer set, useful for small cohorts where the empirical histogram is chunky.--mc-seed N— seed for reproducible runs.
When --mc is on the JSON gains a monte_carlo block with the p5..p95
fair-value distribution, the percentile of current and target price
within that distribution, per-driver Spearman sensitivity, and the
underlying driver pools. The rendered note appends a distribution
table, an adaptive "Translation:" line keyed to where the current
price sits inside the IQR, and a sensitivity bar chart.
Drivers are sampled INDEPENDENTLY. True peer growth and margin
correlate (rho ~ 0.3-0.5 historically), so MC tail percentiles
understate slightly. This caveat is surfaced in tier_caveats.
When --mc is off the script's behavior, JSON keys, and rendered
output are byte-identical to the pre-MC release.
Endpoints used
GET /v3/reference/tickers/{ticker}: subject and per-peer ticker details. Market cap, sector, shares outstanding (load-bearing for target_mcap), name. One call per name.GET /v2/snapshot/locale/us/markets/stocks/tickers/{ticker}: current price for the subject and each peer. Cheap; falls back through thelastTrade → day.c → prevDay.c → fmvwaterfall per the API patterns foundation.GET /vX/reference/financials?ticker={ticker}&timeframe=quarterly&limit=8&order=desc: eight quarters of financials per name. Subject reuses these for TTM revenue, operating income, EBITDA margin baseline, balance-sheet long-term debt. Peers feed the cohort distributions.- Optional:
GET /v2/aggs/ticker/{ticker}/range/...for the peer-set correlation fallback (uncurated subjects).
Verify endpoint paths against current docs at massive.com/docs before shipping; field names and versions shift.
Doesn't handle (yet)
- Cash not subtracted from EV. Same simplification as
pitch-comps. Massive's financials endpoint doesn't exposecash_and_equivalentsas a named field. EV ismarket_cap + long_term_debt. The target-implied EV uses the subject's current LTD (the analyst's thesis usually doesn't change capital structure). Documented inmultiple-sanity.md. - Cost of capital hardcoded at 9% in the reverse-DCF. A proper
bottom-up WACC requires Beta, tax rate, marginal cost of debt, and
the equity risk premium, none of which are exposed cleanly by the
current API set. 9% is the rough cross-cap-structure midpoint for
large-cap US equities at current rates and is consistent enough
across the peer cohort that the relative comparison holds.
Documented in
reverse-dcf.mdwith the explicit caveat. - Single-stage terminal model. The reverse-DCF discounts a single terminal EBITDA at the horizon × peer-median EV/EBITDA exit multiple. A multi-stage DCF would let growth decay toward a steady state. The simplification produces a slightly lower implied CAGR than a full DCF for high-growth names (because all the growth has to fit in the explicit horizon); documented and acknowledged.
- 5-year peer CAGR is TTM-based proxy. Pulling a true 5-year
revenue CAGR per peer requires 20 quarters of financials per name.
Massive's endpoint supports
limit=20, but for the v1 release the skill uses TTM revenue growth as the peer-cohort proxy. Documented; a clean v2 PR adds true 5y CAGR per peer. - Forward consensus not used. Benzinga has analyst ratings but not consensus estimates in the bundle currently subscribed. When consensus is available, the "assumed growth vs peer band" check could include a "vs consensus growth" comparison; queued for v2.
- Negative-EBITDA peers drop out of the EV/EBITDA distribution
(multiple is meaningless). The peer count in the schema records
n_peers_usedper check so the reader knows the sample size.
These are clean PR extensions. The output schema reserves space for each so adding them later doesn't break consumers.