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
  1. Run the line below. It pulls the whole folder into ~/.claude/skills/company-valuation.
  2. Describe your job in plain words. Claude Code follows the skill from there.
Claude Code — installs the whole folder, not just SKILL.md
npx degit himself65/finance-skills/plugins/market-analysis/skills/company-valuation#main ~/.claude/skills/company-valuation

For one project only, change the path to .claude/skills/company-valuation.

Claude (web or desktop app)
  1. On this page open ⋯ → Download .md.
  2. Save it as SKILL.md in a folder, zip the folder, then Customize → Skills → + → Create skill → Upload a skill.
  3. Pick the file and Save. Claude shows the name and description and runs a security scan.
  4. Check the skill is switched on.
  5. Start a new chat and describe your job in plain words. The AI follows the skill from there.
ChatGPT or another app
  1. ChatGPT: make a Project and paste it into Instructions.
  2. 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.
Step-by-step guide with screenshots · Ask in the forum

Paste into Claude, ChatGPT or Cursor.

Source of Company valuation

Show the full text302 lines
namedescription
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:

  1. DCF — 5-year FCFF projection, discount at WACC, terminal value.
  2. Relative — apply peer median P/E, EV/Revenue, EV/EBITDA.
  3. 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:

  1. 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."
  2. Snapshot — sector, industry, market cap, current price, 3M / 12M price change, LTM revenue growth.
  3. Three-method summary — 3-column table: method | implied price | weight | brief rationale.
  4. DCF build — assumptions table (growth path, margins, WACC components, terminal method) + 5-yr FCFF projection table + EV-to-equity bridge.
  5. 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.
  6. SOTP (if applicable) — segment table + adjustments + equity value.
  7. Sensitivity matrix — WACC × g grid (5×5), base case highlighted.
  8. Scenarios — Bull / Base / Bear table with levers + implied price.
  9. 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 theme
  • references/sotp.md — Sum-of-parts methodology, conglomerate discount detection, catalysts
  • references/wacc_erp_rates.md — Risk-free rates, equity risk premiums, sector WACC benchmarks, sector-default betas
1---
2name: company-valuation
3description: >
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 
20Triangulates intrinsic value via three methods, then blends them to an implied share price:
21 
221. **DCF** — 5-year FCFF projection, discount at WACC, terminal value.
232. **Relative** — apply peer median P/E, EV/Revenue, EV/EBITDA.
243. **SOTP** — when 2+ distinct reporting segments exist, value each at pure-play peer multiples.
25 
26Always 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 
34Detect 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 
54If `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 
74Every 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 
93See `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```python
100import yfinance as yf
101import numpy as np
102import pandas as pd
103 
104TICKER = "AAPL" # replace
105t = yf.Ticker(TICKER)
106 
107info = t.info
108income_a = t.income_stmt
109cashflow_a = t.cashflow
110balance_a = t.balance_sheet
111income_q = t.quarterly_income_stmt
112cashflow_q = t.quarterly_cashflow
113 
114earnings_est = t.earnings_estimate
115revenue_est = t.revenue_estimate
116 
117price = info.get("currentPrice") or info.get("regularMarketPrice")
118market_cap = info.get("marketCap")
119shares_out = info.get("sharesOutstanding")
120total_debt = info.get("totalDebt") or 0
121cash = info.get("totalCash") or 0
122beta = info.get("beta") or 1.0
123sector = info.get("sector")
124industry = info.get("industry")
125```
126 
127Key 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 
143Full methodology + industry-specific tweaks in `references/dcf.md`. Quick skeleton:
144 
145```python
146# 4a. Revenue growth path — fade from Y1 (consensus or hist CAGR) to terminal g
147hist_cagr = (rev[-1] / rev[0]) ** (1 / (len(rev)-1)) - 1
148y1 = float(revenue_est.loc["+1y", "growth"]) if "+1y" in revenue_est.index else hist_cagr
149g_terminal = 0.025
150growth_path = np.linspace(y1, g_terminal + 0.01, 5)
151 
152# 4b. Margins — 3y median
153ebit_margin = float((income_a.loc["Operating Income"] / income_a.loc["Total Revenue"]).iloc[:3].median())
154da_pct = float((cashflow_a.loc["Depreciation And Amortization"] / income_a.loc["Total Revenue"]).iloc[:3].median())
155capex_pct = float((cashflow_a.loc["Capital Expenditure"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median())
156nwc_pct = float((cashflow_a.loc["Change In Working Capital"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median())
157tax_rate = max(0.15, min(0.30, 0.21)) # use effective if available
158 
159# 4c. FCFF per year
160rev_t = [float(income_a.loc["Total Revenue"].iloc[0])]
161fcff = []
162for 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
169rf, erp, kd = 0.045, 0.055, 0.055 # override rf with live value from Step 1
170ke = rf + beta * erp
171e_v = market_cap / (market_cap + total_debt)
172d_v = 1 - e_v
173wacc = e_v*ke + d_v*kd*(1 - tax_rate)
174 
175# 4e. Terminal value — compute both, use midpoint
176tv_gordon = fcff[-1] * (1 + g_terminal) / (wacc - g_terminal)
177tv_exit = (rev_t[-1] * ebit_margin + rev_t[-1] * da_pct) * 15 # peer median EV/EBITDA
178tv_base = 0.5 * (tv_gordon + tv_exit)
179 
180# 4f. Bridge to equity
181pv_fcff = sum(f / (1+wacc)**(i+1) for i, f in enumerate(fcff))
182pv_tv = tv_base / (1+wacc)**5
183ev = pv_fcff + pv_tv
184equity = ev + cash - total_debt
185implied_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 
194Select 4-6 peers. Peer map and adjustment rules in `references/relative_valuation.md`.
195 
196```python
197PEERS = ["MSFT", "ORCL", "CRM", "NOW", "SAP", "WDAY"] # pick by industry
198multiples = {}
199for 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 }
207med_pe = np.nanmedian([v["pe_fwd"] for v in multiples.values()])
208med_ev_rev = np.nanmedian([v["ev_rev"] for v in multiples.values()])
209med_ev_eb = np.nanmedian([v["ev_ebitda"] for v in multiples.values()])
210 
211eps_ttm = float(income_q.loc["Diluted EPS"].iloc[:4].sum())
212rev_ttm = float(income_q.loc["Total Revenue"].iloc[:4].sum())
213ebitda_ttm = float(income_q.loc["EBIT"].iloc[:4].sum()) + float(cashflow_q.loc["Depreciation And Amortization"].iloc[:4].sum())
214net_debt = total_debt - cash
215 
216implied_pe = med_pe * eps_ttm
217implied_ev_rev = (med_ev_rev * rev_ttm - net_debt) / shares_out
218implied_ev_ebit = (med_ev_eb * ebitda_ttm - net_debt) / shares_out
219implied_price_rel = np.nanmedian([implied_pe, implied_ev_rev, implied_ev_ebit])
220```
221 
222Adjust 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 
228Skip 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 
234SOTP discount = (SOTP price − market price) / SOTP price. Flag if >20% (conglomerate discount).
235 
236---
237 
238## Step 7: Triangulate, Sensitivity, Scenarios
239 
240```python
241# Blended implied price
242if sotp_price is None:
243 blended = 0.5*implied_price_dcf + 0.5*implied_price_rel
244else:
245 blended = 0.4*implied_price_dcf + 0.3*implied_price_rel + 0.3*sotp_price
246 
247# 5x5 sensitivity grid
248wacc_grid = [wacc + dx for dx in (-0.01, -0.005, 0, 0.005, 0.01)]
249g_grid = [0.015, 0.020, 0.025, 0.030, 0.035]
250sens = {}
251for 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 
258Also 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 
264Output in this order:
265 
2661. **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."
2672. **Snapshot** — sector, industry, market cap, current price, 3M / 12M price change, LTM revenue growth.
2683. **Three-method summary** — 3-column table: method | implied price | weight | brief rationale.
2694. **DCF build** — assumptions table (growth path, margins, WACC components, terminal method) + 5-yr FCFF projection table + EV-to-equity bridge.
2705. **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.
2716. **SOTP** (if applicable) — segment table + adjustments + equity value.
2727. **Sensitivity matrix** — WACC × g grid (5×5), base case highlighted.
2738. **Scenarios** — Bull / Base / Bear table with levers + implied price.
2749. **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