Company valuation
Estimate the intrinsic value of a public company using DCF, relative (peer multiple) and sum-of-parts (SOTP) methods, then triangulate to an implied share price with upside/downside versus the current market price.
How to use it
Claude Code
- Run the line below. It pulls the whole folder into
~/.claude/skills/company-valuation. - Describe your job in plain words. Claude Code follows the skill from there.
npx degit himself65/finance-skills/plugins/market-analysis/skills/company-valuation#main ~/.claude/skills/company-valuationFor one project only, change the path to .claude/skills/company-valuation.
Claude (web or desktop app)
- On this page open ⋯ → Download .md.
- Save it as SKILL.md in a folder, zip the folder, then Customize → Skills → + → Create skill → Upload a skill.
- Pick the file and Save. Claude shows the name and description and runs a security scan.
- Check the skill is switched on.
- Start a new chat and describe your job in plain words. The AI follows the skill from there.
ChatGPT or another app
- ChatGPT: make a Project and paste it into Instructions.
- Neither? Paste it at the top of a new chat — it works for that chat.
Not working?
- Check which app you pasted it into — the steps above name the right one.
- Some skills need the paid tier of Claude or ChatGPT.
Paste into Claude, ChatGPT or Cursor.
Source of Company valuation
Show the full text302 lines
| name | description |
|---|---|
| company-valuation | > Estimate the intrinsic value of a public company using DCF, relative (peer multiple) and sum-of-parts (SOTP) methods, then triangulate to an implied share price with upside/downside versus the current market price. Use this skill whenever the user asks: what is AAPL worth", "valuation of NVDA", "fair value of TSLA", "intrinsic value", DCF for MSFT", "build a DCF", "discounted cash flow", "WACC", "terminal value", implied share price", "upside to fair value", "is X overvalued/undervalued", relative valuation", "peer comparison valuation", "EV/EBITDA target", "SOTP", sum of the parts", "how much is [company] worth", "price target from fundamentals", value this company", or any ticker in the context of computing intrinsic or relative valuation. Default to running ALL three methods (DCF + relative + SOTP-if-applicable) and presenting a blended implied price with a sensitivity table. Do not answer valuation questions from memory — always run the workflow. |
Company Valuation
Triangulates intrinsic value via three methods, then blends them to an implied share price:
- DCF — 5-year FCFF projection, discount at WACC, terminal value.
- Relative — apply peer median P/E, EV/Revenue, EV/EBITDA.
- SOTP — when 2+ distinct reporting segments exist, value each at pure-play peer multiples.
Always present a WACC × terminal-growth sensitivity table and Bull/Base/Bear scenarios.
Disclaimer: Research/educational output. Not financial advice.
Step 1: Detection Flow
Detect data source and runtime deps. The skill supports 2 method paths — pick the richest one available.
Environment status:
!`python3 -c "exec('try:\n import yfinance, numpy, pandas\n print(\'YFIN_OK\')\nexcept Exception:\n print(\'YFIN_MISSING\')')"`
!`python3 -c "exec('try:\n import yfinance as yf\n t=yf.Ticker(\'^TNX\')\n p=t.fast_info.last_price\n print(f\'RF_10Y={p/100:.4f}\')\nexcept Exception:\n print(\'RF_FETCH_FAIL\')')"`
Decision tree:
| Condition | Method path |
|---|---|
YFIN_OK |
Path A (primary): yfinance for financials + peer multiples |
YFIN_MISSING |
Path B: pip-install yfinance, then Path A. python3 -m pip install -q yfinance numpy pandas |
RF_FETCH_FAIL |
Use default rf = 0.045 and note stale risk-free rate in output |
If RF_10Y= printed, use that value as rf in Step 4d instead of the hardcoded 4.5%.
Step 2: Choose Methods & Set Defaults
Method applicability
| Company type | DCF | Relative | SOTP | Fallback |
|---|---|---|---|---|
| Mature cash-flow (CPG, telecom, utilities) | ✅ primary | ✅ | ❌ | — |
| High-growth SaaS / software | ✅ with care | ✅ primary | ❌ | Use EV/Revenue + Rule of 40 |
| Multi-segment conglomerate | ✅ | ✅ | ✅ primary | See references/sotp.md |
| Banks / insurance | ❌ | ✅ (P/B, P/TBV) | ❌ | DDM or excess return; note in output |
| Pre-revenue | ❌ | EV/Revenue only | ❌ | Flag low confidence |
| REITs | ❌ | ✅ (P/FFO, P/AFFO) | ❌ | NAV-based |
| Cyclicals (energy, semis, industrials) | ✅ on mid-cycle | ✅ | sometimes | Normalize through-cycle |
Defaults table
Every parameter below MUST have a value before moving to Step 3. Use these unless the user overrides.
| Parameter | Default | Rationale |
|---|---|---|
| Projection horizon | 5 years | Standard explicit forecast window |
Terminal growth g |
2.5% | ~ long-run US GDP |
Risk-free rate rf |
Live 10Y UST from Step 1, else 4.5% | Current cost of capital anchor |
Equity risk premium erp |
5.5% | Damodaran mid-range |
| Beta | info['beta'] from yfinance |
Market-observed levered beta |
Cost of debt kd |
interest_expense / total_debt, else 5.5% |
Effective rate; fallback to IG spread |
| Tax rate | 3-yr median effective rate, floored 15%, capped 30% | Strips out one-offs |
| Margin assumptions | 3-yr median of each ratio | Smooths cyclical noise |
| SBC treatment | Cash for software/SaaS; non-cash for industrials/CPG | Industry convention |
| Peer count | 4-6 | Balances signal vs noise |
| Peer multiple | Median (not mean) | Robust to outliers |
| Method weights (no SOTP) | DCF 50% / Relative 50% | Equal triangulation |
| Method weights (with SOTP) | DCF 40% / Relative 30% / SOTP 30% | SOTP gets weight when applicable |
| Sensitivity grid | WACC ±1% in 0.5% steps × g from 1.5-3.5% in 0.5% | 5×5 matrix |
See references/wacc_erp_rates.md for current risk-free rates, ERP tables, and sector WACC benchmarks.
Step 3: Pull Data
import yfinance as yf
import numpy as np
import pandas as pd
TICKER = "AAPL" # replace
t = yf.Ticker(TICKER)
info = t.info
income_a = t.income_stmt
cashflow_a = t.cashflow
balance_a = t.balance_sheet
income_q = t.quarterly_income_stmt
cashflow_q = t.quarterly_cashflow
earnings_est = t.earnings_estimate
revenue_est = t.revenue_estimate
price = info.get("currentPrice") or info.get("regularMarketPrice")
market_cap = info.get("marketCap")
shares_out = info.get("sharesOutstanding")
total_debt = info.get("totalDebt") or 0
cash = info.get("totalCash") or 0
beta = info.get("beta") or 1.0
sector = info.get("sector")
industry = info.get("industry")
Key financial statement rows (yfinance labels):
| Need | Row |
|---|---|
| Revenue | Total Revenue |
| EBIT | Operating Income |
| Net income | Net Income |
| D&A | Depreciation And Amortization (in cashflow) |
| CapEx | Capital Expenditure (negative) |
| ΔNWC | Change In Working Capital (cashflow) |
| SBC | Stock Based Compensation (cashflow) |
Step 4: DCF Build
Full methodology + industry-specific tweaks in references/dcf.md. Quick skeleton:
# 4a. Revenue growth path — fade from Y1 (consensus or hist CAGR) to terminal g
hist_cagr = (rev[-1] / rev[0]) ** (1 / (len(rev)-1)) - 1
y1 = float(revenue_est.loc["+1y", "growth"]) if "+1y" in revenue_est.index else hist_cagr
g_terminal = 0.025
growth_path = np.linspace(y1, g_terminal + 0.01, 5)
# 4b. Margins — 3y median
ebit_margin = float((income_a.loc["Operating Income"] / income_a.loc["Total Revenue"]).iloc[:3].median())
da_pct = float((cashflow_a.loc["Depreciation And Amortization"] / income_a.loc["Total Revenue"]).iloc[:3].median())
capex_pct = float((cashflow_a.loc["Capital Expenditure"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median())
nwc_pct = float((cashflow_a.loc["Change In Working Capital"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median())
tax_rate = max(0.15, min(0.30, 0.21)) # use effective if available
# 4c. FCFF per year
rev_t = [float(income_a.loc["Total Revenue"].iloc[0])]
fcff = []
for g in growth_path:
rev_t.append(rev_t[-1] * (1 + g))
ebit = rev_t[-1] * ebit_margin
nopat = ebit * (1 - tax_rate)
fcff.append(nopat + rev_t[-1]*da_pct - rev_t[-1]*capex_pct - rev_t[-1]*nwc_pct)
# 4d. WACC
rf, erp, kd = 0.045, 0.055, 0.055 # override rf with live value from Step 1
ke = rf + beta * erp
e_v = market_cap / (market_cap + total_debt)
d_v = 1 - e_v
wacc = e_v*ke + d_v*kd*(1 - tax_rate)
# 4e. Terminal value — compute both, use midpoint
tv_gordon = fcff[-1] * (1 + g_terminal) / (wacc - g_terminal)
tv_exit = (rev_t[-1] * ebit_margin + rev_t[-1] * da_pct) * 15 # peer median EV/EBITDA
tv_base = 0.5 * (tv_gordon + tv_exit)
# 4f. Bridge to equity
pv_fcff = sum(f / (1+wacc)**(i+1) for i, f in enumerate(fcff))
pv_tv = tv_base / (1+wacc)**5
ev = pv_fcff + pv_tv
equity = ev + cash - total_debt
implied_price_dcf = equity / shares_out
Gates: (a) if wacc <= g_terminal → stop, g too aggressive; (b) if pv_tv / ev > 0.85 or < 0.45 → flag and show both TV methods; (c) if wacc is outside the sector sanity band in references/wacc_erp_rates.md → note.
Step 5: Relative Valuation
Select 4-6 peers. Peer map and adjustment rules in references/relative_valuation.md.
PEERS = ["MSFT", "ORCL", "CRM", "NOW", "SAP", "WDAY"] # pick by industry
multiples = {}
for p in PEERS:
pi = yf.Ticker(p).info
multiples[p] = {
"pe_fwd": pi.get("forwardPE"),
"ev_rev": pi.get("enterpriseToRevenue"),
"ev_ebitda": pi.get("enterpriseToEbitda"),
"ps": pi.get("priceToSalesTrailing12Months"),
}
med_pe = np.nanmedian([v["pe_fwd"] for v in multiples.values()])
med_ev_rev = np.nanmedian([v["ev_rev"] for v in multiples.values()])
med_ev_eb = np.nanmedian([v["ev_ebitda"] for v in multiples.values()])
eps_ttm = float(income_q.loc["Diluted EPS"].iloc[:4].sum())
rev_ttm = float(income_q.loc["Total Revenue"].iloc[:4].sum())
ebitda_ttm = float(income_q.loc["EBIT"].iloc[:4].sum()) + float(cashflow_q.loc["Depreciation And Amortization"].iloc[:4].sum())
net_debt = total_debt - cash
implied_pe = med_pe * eps_ttm
implied_ev_rev = (med_ev_rev * rev_ttm - net_debt) / shares_out
implied_ev_ebit = (med_ev_eb * ebitda_ttm - net_debt) / shares_out
implied_price_rel = np.nanmedian([implied_pe, implied_ev_rev, implied_ev_ebit])
Adjust peer median ±10-30% if target's growth or margin profile diverges materially. Always state the adjustment and reason. Rule of 40 anchor for SaaS in references/relative_valuation.md.
Step 6: SOTP (multi-segment only)
Skip unless the 10-K reports 2+ operating segments with distinct economics. yfinance does NOT expose segment data — user must supply or parse from filings. Full methodology in references/sotp.md:
- Identify segments + pure-play peer for each
- Apply peer median EV/EBITDA (or EV/Rev for growth segments)
- Subtract unallocated corporate costs (cap 2-5% of revenue if unknown)
- Subtract net debt, minority interest; divide by shares
SOTP discount = (SOTP price − market price) / SOTP price. Flag if >20% (conglomerate discount).
Step 7: Triangulate, Sensitivity, Scenarios
# Blended implied price
if sotp_price is None:
blended = 0.5*implied_price_dcf + 0.5*implied_price_rel
else:
blended = 0.4*implied_price_dcf + 0.3*implied_price_rel + 0.3*sotp_price
# 5x5 sensitivity grid
wacc_grid = [wacc + dx for dx in (-0.01, -0.005, 0, 0.005, 0.01)]
g_grid = [0.015, 0.020, 0.025, 0.030, 0.035]
sens = {}
for w in wacc_grid:
for g in g_grid:
tv = fcff[-1]*(1+g)/(w-g)
pv = sum(f/(1+w)**(i+1) for i,f in enumerate(fcff)) + tv/(1+w)**5
sens[(w,g)] = (pv + cash - total_debt) / shares_out
Also produce Bull / Base / Bear: shift revenue growth ±300bps, EBIT margin ±200bps, WACC ∓100bps, terminal g 3.0% / 2.5% / 1.5%.
Step 8: Respond to the User
Output in this order:
- Headline verdict — one sentence: blended fair value, vs. current, % upside/downside, most bullish/bearish method. Example: "AAPL fair value ≈ $215 (blended), vs. current $198 → ~9% upside; DCF is most bullish at $228."
- Snapshot — sector, industry, market cap, current price, 3M / 12M price change, LTM revenue growth.
- Three-method summary — 3-column table: method | implied price | weight | brief rationale.
- DCF build — assumptions table (growth path, margins, WACC components, terminal method) + 5-yr FCFF projection table + EV-to-equity bridge.
- Peer comparison — table of peers with P/E fwd, EV/Rev, EV/EBITDA, gross margin, rev growth; bottom row = median; flag target's premium/discount.
- SOTP (if applicable) — segment table + adjustments + equity value.
- Sensitivity matrix — WACC × g grid (5×5), base case highlighted.
- Scenarios — Bull / Base / Bear table with levers + implied price.
- Key risks — 3-5 bullets: which assumption moves the answer most; what could break the thesis.
Error handling
| Missing / edge case | Action |
|---|---|
yfinance returns None for beta |
Use sector-default beta from references/wacc_erp_rates.md |
| Negative LTM EBITDA | Skip EV/EBITDA multiple; rely on EV/Revenue + DCF |
| Negative LTM EPS | Skip P/E multiple; use forward P/E if positive, else skip |
| Growth > WACC in Gordon | Cap g = wacc − 0.5% and flag |
| Fewer than 3 years history | Use what's available; flag data confidence as "low" |
| Peer data fetch fails | Drop that peer from median; note in output |
| No segment data for SOTP | Skip Section 6; proceed with DCF + Relative only |
Caveats to include
- TTM data lags real-time; peer multiples reflect market sentiment (can overshoot)
- DCF is garbage-in/garbage-out; sensitivity matters more than a point estimate
- yfinance data is unofficial; cross-check any decision with primary filings
- Not financial advice
Reference Files
references/dcf.md— DCF methodology + industry-specific guidance (software, retail, financials, healthcare, energy, manufacturing, CPG, telecom, REITs, streaming)references/relative_valuation.md— Peer selection, multiple adjustment rules, Rule of 40, peer sets by themereferences/sotp.md— Sum-of-parts methodology, conglomerate discount detection, catalystsreferences/wacc_erp_rates.md— Risk-free rates, equity risk premiums, sector WACC benchmarks, sector-default betas
| 1 | |
| 2 | name company-valuation |
| 3 | description > |
| 4 | Estimate the intrinsic value of a public company using DCF, relative (peer multiple) |
| 5 | and sum-of-parts (SOTP) methods, then triangulate to an implied share price with |
| 6 | upside/downside versus the current market price. Use this skill whenever the user asks: |
| 7 | "what is AAPL worth", "valuation of NVDA", "fair value of TSLA", "intrinsic value", |
| 8 | "DCF for MSFT", "build a DCF", "discounted cash flow", "WACC", "terminal value", |
| 9 | "implied share price", "upside to fair value", "is X overvalued/undervalued", |
| 10 | "relative valuation", "peer comparison valuation", "EV/EBITDA target", "SOTP", |
| 11 | "sum of the parts", "how much is [company] worth", "price target from fundamentals", |
| 12 | "value this company", or any ticker in the context of computing intrinsic or |
| 13 | relative valuation. Default to running ALL three methods |
| 14 | (DCF + relative + SOTP-if-applicable) and presenting a blended implied price with a |
| 15 | sensitivity table. Do not answer valuation questions from memory — always run the workflow. |
| 16 | |
| 17 | |
| 18 | # Company Valuation |
| 19 | |
| 20 | Triangulates intrinsic value via three methods, then blends them to an implied share price: |
| 21 | |
| 22 | **DCF** — 5-year FCFF projection, discount at WACC, terminal value. |
| 23 | **Relative** — apply peer median P/E, EV/Revenue, EV/EBITDA. |
| 24 | **SOTP** — when 2+ distinct reporting segments exist, value each at pure-play peer multiples. |
| 25 | |
| 26 | Always present a WACC × terminal-growth sensitivity table and Bull/Base/Bear scenarios. |
| 27 | |
| 28 | **Disclaimer**: Research/educational output. Not financial advice. |
| 29 | |
| 30 | |
| 31 | |
| 32 | ## Step 1: Detection Flow |
| 33 | |
| 34 | Detect data source and runtime deps. The skill supports 2 method paths — pick the richest one available. |
| 35 | |
| 36 | **Environment status:** |
| 37 | |
| 38 | |
| 39 | !`python3 -c "exec('try:\n import yfinance, numpy, pandas\n print(\'YFIN_OK\')\nexcept Exception:\n print(\'YFIN_MISSING\')')"` |
| 40 | |
| 41 | |
| 42 | |
| 43 | !`python3 -c "exec('try:\n import yfinance as yf\n t=yf.Ticker(\'^TNX\')\n p=t.fast_info.last_price\n print(f\'RF_10Y={p/100:.4f}\')\nexcept Exception:\n print(\'RF_FETCH_FAIL\')')"` |
| 44 | |
| 45 | |
| 46 | **Decision tree:** |
| 47 | |
| 48 | | Condition | Method path | |
| 49 | |---|---| |
| 50 | | `YFIN_OK` | **Path A** (primary): yfinance for financials + peer multiples | |
| 51 | | `YFIN_MISSING` | **Path B**: pip-install yfinance, then Path A. `python3 -m pip install -q yfinance numpy pandas` | |
| 52 | | `RF_FETCH_FAIL` | Use default `rf = 0.045` and note stale risk-free rate in output | |
| 53 | |
| 54 | If `RF_10Y=` printed, use that value as `rf` in Step 4d instead of the hardcoded 4.5%. |
| 55 | |
| 56 | |
| 57 | |
| 58 | ## Step 2: Choose Methods & Set Defaults |
| 59 | |
| 60 | ### Method applicability |
| 61 | |
| 62 | | Company type | DCF | Relative | SOTP | Fallback | |
| 63 | |---|---|---|---|---| |
| 64 | | Mature cash-flow (CPG, telecom, utilities) | ✅ primary | ✅ | ❌ | — | |
| 65 | | High-growth SaaS / software | ✅ with care | ✅ primary | ❌ | Use EV/Revenue + Rule of 40 | |
| 66 | | Multi-segment conglomerate | ✅ | ✅ | ✅ primary | See `references/sotp.md` | |
| 67 | | Banks / insurance | ❌ | ✅ (P/B, P/TBV) | ❌ | DDM or excess return; note in output | |
| 68 | | Pre-revenue | ❌ | EV/Revenue only | ❌ | Flag low confidence | |
| 69 | | REITs | ❌ | ✅ (P/FFO, P/AFFO) | ❌ | NAV-based | |
| 70 | | Cyclicals (energy, semis, industrials) | ✅ on mid-cycle | ✅ | sometimes | Normalize through-cycle | |
| 71 | |
| 72 | ### Defaults table |
| 73 | |
| 74 | Every parameter below MUST have a value before moving to Step 3. Use these unless the user overrides. |
| 75 | |
| 76 | | Parameter | Default | Rationale | |
| 77 | |---|---|---| |
| 78 | | Projection horizon | 5 years | Standard explicit forecast window | |
| 79 | | Terminal growth `g` | 2.5% | ~ long-run US GDP | |
| 80 | | Risk-free rate `rf` | Live 10Y UST from Step 1, else 4.5% | Current cost of capital anchor | |
| 81 | | Equity risk premium `erp` | 5.5% | Damodaran mid-range | |
| 82 | | Beta | `info['beta']` from yfinance | Market-observed levered beta | |
| 83 | | Cost of debt `kd` | `interest_expense / total_debt`, else 5.5% | Effective rate; fallback to IG spread | |
| 84 | | Tax rate | 3-yr median effective rate, floored 15%, capped 30% | Strips out one-offs | |
| 85 | | Margin assumptions | 3-yr median of each ratio | Smooths cyclical noise | |
| 86 | | SBC treatment | Cash for software/SaaS; non-cash for industrials/CPG | Industry convention | |
| 87 | | Peer count | 4-6 | Balances signal vs noise | |
| 88 | | Peer multiple | Median (not mean) | Robust to outliers | |
| 89 | | Method weights (no SOTP) | DCF 50% / Relative 50% | Equal triangulation | |
| 90 | | Method weights (with SOTP) | DCF 40% / Relative 30% / SOTP 30% | SOTP gets weight when applicable | |
| 91 | | Sensitivity grid | WACC ±1% in 0.5% steps × g from 1.5-3.5% in 0.5% | 5×5 matrix | |
| 92 | |
| 93 | See `references/wacc_erp_rates.md` for current risk-free rates, ERP tables, and sector WACC benchmarks. |
| 94 | |
| 95 | |
| 96 | |
| 97 | ## Step 3: Pull Data |
| 98 | |
| 99 | |
| 100 | import yfinance as yf |
| 101 | import numpy as np |
| 102 | import pandas as pd |
| 103 | |
| 104 | TICKER = "AAPL" # replace |
| 105 | t = yf.Ticker(TICKER) |
| 106 | |
| 107 | info = t.info |
| 108 | income_a = t.income_stmt |
| 109 | cashflow_a = t.cashflow |
| 110 | balance_a = t.balance_sheet |
| 111 | income_q = t.quarterly_income_stmt |
| 112 | cashflow_q = t.quarterly_cashflow |
| 113 | |
| 114 | earnings_est = t.earnings_estimate |
| 115 | revenue_est = t.revenue_estimate |
| 116 | |
| 117 | price = info.get("currentPrice") or info.get("regularMarketPrice") |
| 118 | market_cap = info.get("marketCap") |
| 119 | shares_out = info.get("sharesOutstanding") |
| 120 | total_debt = info.get("totalDebt") or 0 |
| 121 | cash = info.get("totalCash") or 0 |
| 122 | beta = info.get("beta") or 1.0 |
| 123 | sector = info.get("sector") |
| 124 | industry = info.get("industry") |
| 125 | |
| 126 | |
| 127 | Key financial statement rows (yfinance labels): |
| 128 | |
| 129 | | Need | Row | |
| 130 | |---|---| |
| 131 | | Revenue | `Total Revenue` | |
| 132 | | EBIT | `Operating Income` | |
| 133 | | Net income | `Net Income` | |
| 134 | | D&A | `Depreciation And Amortization` (in cashflow) | |
| 135 | | CapEx | `Capital Expenditure` (negative) | |
| 136 | | ΔNWC | `Change In Working Capital` (cashflow) | |
| 137 | | SBC | `Stock Based Compensation` (cashflow) | |
| 138 | |
| 139 | |
| 140 | |
| 141 | ## Step 4: DCF Build |
| 142 | |
| 143 | Full methodology + industry-specific tweaks in `references/dcf.md`. Quick skeleton: |
| 144 | |
| 145 | |
| 146 | # 4a. Revenue growth path — fade from Y1 (consensus or hist CAGR) to terminal g |
| 147 | hist_cagr = (rev[-1] / rev[0]) ** (1 / (len(rev)-1)) - 1 |
| 148 | y1 = float(revenue_est.loc["+1y", "growth"]) if "+1y" in revenue_est.index else hist_cagr |
| 149 | g_terminal = 0.025 |
| 150 | growth_path = np.linspace(y1, g_terminal + 0.01, 5) |
| 151 | |
| 152 | # 4b. Margins — 3y median |
| 153 | ebit_margin = float((income_a.loc["Operating Income"] / income_a.loc["Total Revenue"]).iloc[:3].median()) |
| 154 | da_pct = float((cashflow_a.loc["Depreciation And Amortization"] / income_a.loc["Total Revenue"]).iloc[:3].median()) |
| 155 | capex_pct = float((cashflow_a.loc["Capital Expenditure"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median()) |
| 156 | nwc_pct = float((cashflow_a.loc["Change In Working Capital"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median()) |
| 157 | tax_rate = max(0.15, min(0.30, 0.21)) # use effective if available |
| 158 | |
| 159 | # 4c. FCFF per year |
| 160 | rev_t = [float(income_a.loc["Total Revenue"].iloc[0])] |
| 161 | fcff = [] |
| 162 | for g in growth_path: |
| 163 | rev_t.append(rev_t[-1] * (1 + g)) |
| 164 | ebit = rev_t[-1] * ebit_margin |
| 165 | nopat = ebit * (1 - tax_rate) |
| 166 | fcff.append(nopat + rev_t[-1]*da_pct - rev_t[-1]*capex_pct - rev_t[-1]*nwc_pct) |
| 167 | |
| 168 | # 4d. WACC |
| 169 | rf, erp, kd = 0.045, 0.055, 0.055 # override rf with live value from Step 1 |
| 170 | ke = rf + beta * erp |
| 171 | e_v = market_cap / (market_cap + total_debt) |
| 172 | d_v = 1 - e_v |
| 173 | wacc = e_v*ke + d_v*kd*(1 - tax_rate) |
| 174 | |
| 175 | # 4e. Terminal value — compute both, use midpoint |
| 176 | tv_gordon = fcff[-1] * (1 + g_terminal) / (wacc - g_terminal) |
| 177 | tv_exit = (rev_t[-1] * ebit_margin + rev_t[-1] * da_pct) * 15 # peer median EV/EBITDA |
| 178 | tv_base = 0.5 * (tv_gordon + tv_exit) |
| 179 | |
| 180 | # 4f. Bridge to equity |
| 181 | pv_fcff = sum(f / (1+wacc)**(i+1) for i, f in enumerate(fcff)) |
| 182 | pv_tv = tv_base / (1+wacc)**5 |
| 183 | ev = pv_fcff + pv_tv |
| 184 | equity = ev + cash - total_debt |
| 185 | implied_price_dcf = equity / shares_out |
| 186 | |
| 187 | |
| 188 | **Gates:** (a) if `wacc <= g_terminal` → stop, g too aggressive; (b) if `pv_tv / ev > 0.85` or `< 0.45` → flag and show both TV methods; (c) if `wacc` is outside the sector sanity band in `references/wacc_erp_rates.md` → note. |
| 189 | |
| 190 | |
| 191 | |
| 192 | ## Step 5: Relative Valuation |
| 193 | |
| 194 | Select 4-6 peers. Peer map and adjustment rules in `references/relative_valuation.md`. |
| 195 | |
| 196 | |
| 197 | PEERS = ["MSFT", "ORCL", "CRM", "NOW", "SAP", "WDAY"] # pick by industry |
| 198 | multiples = {} |
| 199 | for p in PEERS: |
| 200 | pi = yf.Ticker(p).info |
| 201 | multiples[p] = { |
| 202 | "pe_fwd": pi.get("forwardPE"), |
| 203 | "ev_rev": pi.get("enterpriseToRevenue"), |
| 204 | "ev_ebitda": pi.get("enterpriseToEbitda"), |
| 205 | "ps": pi.get("priceToSalesTrailing12Months"), |
| 206 | } |
| 207 | med_pe = np.nanmedian([v["pe_fwd"] for v in multiples.values()]) |
| 208 | med_ev_rev = np.nanmedian([v["ev_rev"] for v in multiples.values()]) |
| 209 | med_ev_eb = np.nanmedian([v["ev_ebitda"] for v in multiples.values()]) |
| 210 | |
| 211 | eps_ttm = float(income_q.loc["Diluted EPS"].iloc[:4].sum()) |
| 212 | rev_ttm = float(income_q.loc["Total Revenue"].iloc[:4].sum()) |
| 213 | ebitda_ttm = float(income_q.loc["EBIT"].iloc[:4].sum()) + float(cashflow_q.loc["Depreciation And Amortization"].iloc[:4].sum()) |
| 214 | net_debt = total_debt - cash |
| 215 | |
| 216 | implied_pe = med_pe * eps_ttm |
| 217 | implied_ev_rev = (med_ev_rev * rev_ttm - net_debt) / shares_out |
| 218 | implied_ev_ebit = (med_ev_eb * ebitda_ttm - net_debt) / shares_out |
| 219 | implied_price_rel = np.nanmedian([implied_pe, implied_ev_rev, implied_ev_ebit]) |
| 220 | |
| 221 | |
| 222 | Adjust peer median ±10-30% if target's growth or margin profile diverges materially. Always state the adjustment and reason. Rule of 40 anchor for SaaS in `references/relative_valuation.md`. |
| 223 | |
| 224 | |
| 225 | |
| 226 | ## Step 6: SOTP (multi-segment only) |
| 227 | |
| 228 | Skip unless the 10-K reports 2+ operating segments with distinct economics. yfinance does NOT expose segment data — user must supply or parse from filings. Full methodology in `references/sotp.md`: |
| 229 | Identify segments + pure-play peer for each |
| 230 | Apply peer median EV/EBITDA (or EV/Rev for growth segments) |
| 231 | Subtract unallocated corporate costs (cap 2-5% of revenue if unknown) |
| 232 | Subtract net debt, minority interest; divide by shares |
| 233 | |
| 234 | SOTP discount = (SOTP price − market price) / SOTP price. Flag if >20% (conglomerate discount). |
| 235 | |
| 236 | |
| 237 | |
| 238 | ## Step 7: Triangulate, Sensitivity, Scenarios |
| 239 | |
| 240 | |
| 241 | # Blended implied price |
| 242 | if sotp_price is None: |
| 243 | blended = 0.5*implied_price_dcf + 0.5*implied_price_rel |
| 244 | else: |
| 245 | blended = 0.4*implied_price_dcf + 0.3*implied_price_rel + 0.3*sotp_price |
| 246 | |
| 247 | # 5x5 sensitivity grid |
| 248 | wacc_grid = [wacc + dx for dx in (-0.01, -0.005, 0, 0.005, 0.01)] |
| 249 | g_grid = [0.015, 0.020, 0.025, 0.030, 0.035] |
| 250 | sens = {} |
| 251 | for w in wacc_grid: |
| 252 | for g in g_grid: |
| 253 | tv = fcff[-1]*(1+g)/(w-g) |
| 254 | pv = sum(f/(1+w)**(i+1) for i,f in enumerate(fcff)) + tv/(1+w)**5 |
| 255 | sens[(w,g)] = (pv + cash - total_debt) / shares_out |
| 256 | |
| 257 | |
| 258 | Also produce Bull / Base / Bear: shift revenue growth ±300bps, EBIT margin ±200bps, WACC ∓100bps, terminal g 3.0% / 2.5% / 1.5%. |
| 259 | |
| 260 | |
| 261 | |
| 262 | ## Step 8: Respond to the User |
| 263 | |
| 264 | Output in this order: |
| 265 | |
| 266 | **Headline verdict** — one sentence: blended fair value, vs. current, % upside/downside, most bullish/bearish method. Example: "AAPL fair value ≈ $215 (blended), vs. current $198 → ~9% upside; DCF is most bullish at $228." |
| 267 | **Snapshot** — sector, industry, market cap, current price, 3M / 12M price change, LTM revenue growth. |
| 268 | **Three-method summary** — 3-column table: method | implied price | weight | brief rationale. |
| 269 | **DCF build** — assumptions table (growth path, margins, WACC components, terminal method) + 5-yr FCFF projection table + EV-to-equity bridge. |
| 270 | **Peer comparison** — table of peers with P/E fwd, EV/Rev, EV/EBITDA, gross margin, rev growth; bottom row = median; flag target's premium/discount. |
| 271 | **SOTP** (if applicable) — segment table + adjustments + equity value. |
| 272 | **Sensitivity matrix** — WACC × g grid (5×5), base case highlighted. |
| 273 | **Scenarios** — Bull / Base / Bear table with levers + implied price. |
| 274 | **Key risks** — 3-5 bullets: which assumption moves the answer most; what could break the thesis. |
| 275 | |
| 276 | ### Error handling |
| 277 | |
| 278 | | Missing / edge case | Action | |
| 279 | |---|---| |
| 280 | | yfinance returns `None` for beta | Use sector-default beta from `references/wacc_erp_rates.md` | |
| 281 | | Negative LTM EBITDA | Skip EV/EBITDA multiple; rely on EV/Revenue + DCF | |
| 282 | | Negative LTM EPS | Skip P/E multiple; use forward P/E if positive, else skip | |
| 283 | | Growth > WACC in Gordon | Cap `g = wacc − 0.5%` and flag | |
| 284 | | Fewer than 3 years history | Use what's available; flag data confidence as "low" | |
| 285 | | Peer data fetch fails | Drop that peer from median; note in output | |
| 286 | | No segment data for SOTP | Skip Section 6; proceed with DCF + Relative only | |
| 287 | |
| 288 | ### Caveats to include |
| 289 | TTM data lags real-time; peer multiples reflect market sentiment (can overshoot) |
| 290 | DCF is garbage-in/garbage-out; sensitivity matters more than a point estimate |
| 291 | yfinance data is unofficial; cross-check any decision with primary filings |
| 292 | Not financial advice |
| 293 | |
| 294 | |
| 295 | |
| 296 | ## Reference Files |
| 297 | |
| 298 | `references/dcf.md` — DCF methodology + industry-specific guidance (software, retail, financials, healthcare, energy, manufacturing, CPG, telecom, REITs, streaming) |
| 299 | `references/relative_valuation.md` — Peer selection, multiple adjustment rules, Rule of 40, peer sets by theme |
| 300 | `references/sotp.md` — Sum-of-parts methodology, conglomerate discount detection, catalysts |
| 301 | `references/wacc_erp_rates.md` — Risk-free rates, equity risk premiums, sector WACC benchmarks, sector-default betas |
| 302 |
Discussion
Browse more free Claude skills.