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 3 method paths — pick the richest one available.
Environment status:
!`python3 -c "import yfinance, numpy, pandas; print('YFIN_OK')" 2>/dev/null || echo "YFIN_MISSING"`
!`(command -v funda && funda --version) 2>/dev/null || echo "FUNDA_CLI_MISSING"`
!`python3 -c "import yfinance as yf; t=yf.Ticker('^TNX'); p=t.fast_info.last_price; print(f'RF_10Y={p/100:.4f}')" 2>/dev/null || echo "RF_FETCH_FAIL"`
Decision tree:
| Condition |
Method path |
YFIN_OK |
Path A (primary): yfinance for financials + peer multiples |
YFIN_MISSING but FUNDA_CLI_MISSING is not set |
Path B: delegate to finance-data-providers:funda-data skill for fundamentals |
| Both missing |
Path C: 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 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-valuation3description: 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.4---56# Company Valuation78Triangulates intrinsic value via three methods, then blends them to an implied share price:9101. **DCF** — 5-year FCFF projection, discount at WACC, terminal value.112. **Relative** — apply peer median P/E, EV/Revenue, EV/EBITDA.123. **SOTP** — when 2+ distinct reporting segments exist, value each at pure-play peer multiples.1314Always present a WACC × terminal-growth sensitivity table and Bull/Base/Bear scenarios.1516**Disclaimer**: Research/educational output. Not financial advice.1718---1920## Step 1: Detection Flow2122Detect data source and runtime deps. The skill supports 3 method paths — pick the richest one available.2324**Environment status:**2526```27!`python3 -c "import yfinance, numpy, pandas; print('YFIN_OK')" 2>/dev/null || echo "YFIN_MISSING"`28```2930```31!`(command -v funda && funda --version) 2>/dev/null || echo "FUNDA_CLI_MISSING"`32```3334```35!`python3 -c "import yfinance as yf; t=yf.Ticker('^TNX'); p=t.fast_info.last_price; print(f'RF_10Y={p/100:.4f}')" 2>/dev/null || echo "RF_FETCH_FAIL"`36```3738**Decision tree:**3940| Condition | Method path |41|---|---|42| `YFIN_OK` | **Path A** (primary): yfinance for financials + peer multiples |43| `YFIN_MISSING` but `FUNDA_CLI_MISSING` is not set | **Path B**: delegate to `finance-data-providers:funda-data` skill for fundamentals |44| Both missing | **Path C**: pip-install yfinance, then Path A. `python3 -m pip install -q yfinance numpy pandas` |45| `RF_FETCH_FAIL` | Use default `rf = 0.045` and note stale risk-free rate in output |4647If `RF_10Y=` printed, use that value as `rf` in Step 4d instead of the hardcoded 4.5%.4849---5051## Step 2: Choose Methods & Set Defaults5253### Method applicability5455| Company type | DCF | Relative | SOTP | Fallback |56|---|---|---|---|---|57| Mature cash-flow (CPG, telecom, utilities) | ✅ primary | ✅ | ❌ | — |58| High-growth SaaS / software | ✅ with care | ✅ primary | ❌ | Use EV/Revenue + Rule of 40 |59| Multi-segment conglomerate | ✅ | ✅ | ✅ primary | See `references/sotp.md` |60| Banks / insurance | ❌ | ✅ (P/B, P/TBV) | ❌ | DDM or excess return; note in output |61| Pre-revenue | ❌ | EV/Revenue only | ❌ | Flag low confidence |62| REITs | ❌ | ✅ (P/FFO, P/AFFO) | ❌ | NAV-based |63| Cyclicals (energy, semis, industrials) | ✅ on mid-cycle | ✅ | sometimes | Normalize through-cycle |6465### Defaults table6667Every parameter below MUST have a value before moving to Step 3. Use these unless the user overrides.6869| Parameter | Default | Rationale |70|---|---|---|71| Projection horizon | 5 years | Standard explicit forecast window |72| Terminal growth `g` | 2.5% | ~ long-run US GDP |73| Risk-free rate `rf` | Live 10Y UST from Step 1, else 4.5% | Current cost of capital anchor |74| Equity risk premium `erp` | 5.5% | Damodaran mid-range |75| Beta | `info['beta']` from yfinance | Market-observed levered beta |76| Cost of debt `kd` | `interest_expense / total_debt`, else 5.5% | Effective rate; fallback to IG spread |77| Tax rate | 3-yr median effective rate, floored 15%, capped 30% | Strips out one-offs |78| Margin assumptions | 3-yr median of each ratio | Smooths cyclical noise |79| SBC treatment | Cash for software/SaaS; non-cash for industrials/CPG | Industry convention |80| Peer count | 4-6 | Balances signal vs noise |81| Peer multiple | Median (not mean) | Robust to outliers |82| Method weights (no SOTP) | DCF 50% / Relative 50% | Equal triangulation |83| Method weights (with SOTP) | DCF 40% / Relative 30% / SOTP 30% | SOTP gets weight when applicable |84| Sensitivity grid | WACC ±1% in 0.5% steps × g from 1.5-3.5% in 0.5% | 5×5 matrix |8586See `references/wacc_erp_rates.md` for current risk-free rates, ERP tables, and sector WACC benchmarks.8788---8990## Step 3: Pull Data9192```python93import yfinance as yf94import numpy as np95import pandas as pd9697TICKER = "AAPL" # replace98t = yf.Ticker(TICKER)99100info = t.info101income_a = t.income_stmt102cashflow_a = t.cashflow103balance_a = t.balance_sheet104income_q = t.quarterly_income_stmt105cashflow_q = t.quarterly_cashflow106107earnings_est = t.earnings_estimate108revenue_est = t.revenue_estimate109110price = info.get("currentPrice") or info.get("regularMarketPrice")111market_cap = info.get("marketCap")112shares_out = info.get("sharesOutstanding")113total_debt = info.get("totalDebt") or 0114cash = info.get("totalCash") or 0115beta = info.get("beta") or 1.0116sector = info.get("sector")117industry = info.get("industry")118```119120Key financial statement rows (yfinance labels):121122| Need | Row |123|---|---|124| Revenue | `Total Revenue` |125| EBIT | `Operating Income` |126| Net income | `Net Income` |127| D&A | `Depreciation And Amortization` (in cashflow) |128| CapEx | `Capital Expenditure` (negative) |129| ΔNWC | `Change In Working Capital` (cashflow) |130| SBC | `Stock Based Compensation` (cashflow) |131132---133134## Step 4: DCF Build135136Full methodology + industry-specific tweaks in `references/dcf.md`. Quick skeleton:137138```python139# 4a. Revenue growth path — fade from Y1 (consensus or hist CAGR) to terminal g140hist_cagr = (rev[-1] / rev[0]) ** (1 / (len(rev)-1)) - 1141y1 = float(revenue_est.loc["+1y", "growth"]) if "+1y" in revenue_est.index else hist_cagr142g_terminal = 0.025143growth_path = np.linspace(y1, g_terminal + 0.01, 5)144145# 4b. Margins — 3y median146ebit_margin = float((income_a.loc["Operating Income"] / income_a.loc["Total Revenue"]).iloc[:3].median())147da_pct = float((cashflow_a.loc["Depreciation And Amortization"] / income_a.loc["Total Revenue"]).iloc[:3].median())148capex_pct = float((cashflow_a.loc["Capital Expenditure"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median())149nwc_pct = float((cashflow_a.loc["Change In Working Capital"].abs() / income_a.loc["Total Revenue"]).iloc[:3].median())150tax_rate = max(0.15, min(0.30, 0.21)) # use effective if available151152# 4c. FCFF per year153rev_t = [float(income_a.loc["Total Revenue"].iloc[0])]154fcff = []155for g in growth_path:156 rev_t.append(rev_t[-1] * (1 + g))157 ebit = rev_t[-1] * ebit_margin158 nopat = ebit * (1 - tax_rate)159 fcff.append(nopat + rev_t[-1]*da_pct - rev_t[-1]*capex_pct - rev_t[-1]*nwc_pct)160161# 4d. WACC162rf, erp, kd = 0.045, 0.055, 0.055 # override rf with live value from Step 1163ke = rf + beta * erp164e_v = market_cap / (market_cap + total_debt)165d_v = 1 - e_v166wacc = e_v*ke + d_v*kd*(1 - tax_rate)167168# 4e. Terminal value — compute both, use midpoint169tv_gordon = fcff[-1] * (1 + g_terminal) / (wacc - g_terminal)170tv_exit = (rev_t[-1] * ebit_margin + rev_t[-1] * da_pct) * 15 # peer median EV/EBITDA171tv_base = 0.5 * (tv_gordon + tv_exit)172173# 4f. Bridge to equity174pv_fcff = sum(f / (1+wacc)**(i+1) for i, f in enumerate(fcff))175pv_tv = tv_base / (1+wacc)**5176ev = pv_fcff + pv_tv177equity = ev + cash - total_debt178implied_price_dcf = equity / shares_out179```180181**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.182183---184185## Step 5: Relative Valuation186187Select 4-6 peers. Peer map and adjustment rules in `references/relative_valuation.md`.188189```python190PEERS = ["MSFT", "ORCL", "CRM", "NOW", "SAP", "WDAY"] # pick by industry191multiples = {}192for p in PEERS:193 pi = yf.Ticker(p).info194 multiples[p] = {195 "pe_fwd": pi.get("forwardPE"),196 "ev_rev": pi.get("enterpriseToRevenue"),197 "ev_ebitda": pi.get("enterpriseToEbitda"),198 "ps": pi.get("priceToSalesTrailing12Months"),199 }200med_pe = np.nanmedian([v["pe_fwd"] for v in multiples.values()])201med_ev_rev = np.nanmedian([v["ev_rev"] for v in multiples.values()])202med_ev_eb = np.nanmedian([v["ev_ebitda"] for v in multiples.values()])203204eps_ttm = float(income_q.loc["Diluted EPS"].iloc[:4].sum())205rev_ttm = float(income_q.loc["Total Revenue"].iloc[:4].sum())206ebitda_ttm = float(income_q.loc["EBIT"].iloc[:4].sum()) + float(cashflow_q.loc["Depreciation And Amortization"].iloc[:4].sum())207net_debt = total_debt - cash208209implied_pe = med_pe * eps_ttm210implied_ev_rev = (med_ev_rev * rev_ttm - net_debt) / shares_out211implied_ev_ebit = (med_ev_eb * ebitda_ttm - net_debt) / shares_out212implied_price_rel = np.nanmedian([implied_pe, implied_ev_rev, implied_ev_ebit])213```214215Adjust 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`.216217---218219## Step 6: SOTP (multi-segment only)220221Skip 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`:222- Identify segments + pure-play peer for each223- Apply peer median EV/EBITDA (or EV/Rev for growth segments)224- Subtract unallocated corporate costs (cap 2-5% of revenue if unknown)225- Subtract net debt, minority interest; divide by shares226227SOTP discount = (SOTP price − market price) / SOTP price. Flag if >20% (conglomerate discount).228229---230231## Step 7: Triangulate, Sensitivity, Scenarios232233```python234# Blended implied price235if sotp_price is None:236 blended = 0.5*implied_price_dcf + 0.5*implied_price_rel237else:238 blended = 0.4*implied_price_dcf + 0.3*implied_price_rel + 0.3*sotp_price239240# 5x5 sensitivity grid241wacc_grid = [wacc + dx for dx in (-0.01, -0.005, 0, 0.005, 0.01)]242g_grid = [0.015, 0.020, 0.025, 0.030, 0.035]243sens = {}244for w in wacc_grid:245 for g in g_grid:246 tv = fcff[-1]*(1+g)/(w-g)247 pv = sum(f/(1+w)**(i+1) for i,f in enumerate(fcff)) + tv/(1+w)**5248 sens[(w,g)] = (pv + cash - total_debt) / shares_out249```250251Also produce Bull / Base / Bear: shift revenue growth ±300bps, EBIT margin ±200bps, WACC ∓100bps, terminal g 3.0% / 2.5% / 1.5%.252253---254255## Step 8: Respond to the User256257Output in this order:2582591. **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."2602. **Snapshot** — sector, industry, market cap, current price, 3M / 12M price change, LTM revenue growth.2613. **Three-method summary** — 3-column table: method | implied price | weight | brief rationale.2624. **DCF build** — assumptions table (growth path, margins, WACC components, terminal method) + 5-yr FCFF projection table + EV-to-equity bridge.2635. **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.2646. **SOTP** (if applicable) — segment table + adjustments + equity value.2657. **Sensitivity matrix** — WACC × g grid (5×5), base case highlighted.2668. **Scenarios** — Bull / Base / Bear table with levers + implied price.2679. **Key risks** — 3-5 bullets: which assumption moves the answer most; what could break the thesis.268269### Error handling270271| Missing / edge case | Action |272|---|---|273| yfinance returns `None` for beta | Use sector-default beta from `references/wacc_erp_rates.md` |274| Negative LTM EBITDA | Skip EV/EBITDA multiple; rely on EV/Revenue + DCF |275| Negative LTM EPS | Skip P/E multiple; use forward P/E if positive, else skip |276| Growth > WACC in Gordon | Cap `g = wacc − 0.5%` and flag |277| Fewer than 3 years history | Use what's available; flag data confidence as "low" |278| Peer data fetch fails | Drop that peer from median; note in output |279| No segment data for SOTP | Skip Section 6; proceed with DCF + Relative only |280281### Caveats to include282- TTM data lags real-time; peer multiples reflect market sentiment (can overshoot)283- DCF is garbage-in/garbage-out; sensitivity matters more than a point estimate284- yfinance data is unofficial; cross-check any decision with primary filings285- Not financial advice286287---288289## Reference Files290291- `references/dcf.md` — DCF methodology + industry-specific guidance (software, retail, financials, healthcare, energy, manufacturing, CPG, telecom, REITs, streaming)292- `references/relative_valuation.md` — Peer selection, multiple adjustment rules, Rule of 40, peer sets by theme293- `references/sotp.md` — Sum-of-parts methodology, conglomerate discount detection, catalysts294- `references/wacc_erp_rates.md` — Risk-free rates, equity risk premiums, sector WACC benchmarks, sector-default betas