Claude Skill

company-valuation

Estimate a public company's intrinsic value with DCF, relative (peer multiple), and sum-of-the-parts (SOTP) methods, then blend them into an implied share price with upside/downside vs the market price, a WACC and terminal-growth sensitivity grid, and bull/base/bear scenarios. Us

LLM Mart · 0 points · 0 views 1 listing impressions 0 install-command copies
Download himself65-finance-skills-plugins_market-analysis_skills_company-valuation-73d554d.zip · 19 KB

Install

skills CLI npx skills add https://github.com/himself65/finance-skills/tree/main/plugins/market-analysis/skills/company-valuation
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install himself65-finance-skills@llmmart
Git git clone https://github.com/himself65/finance-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole himself65/finance-skills collection as a plugin from our marketplace. Git is the plain clone.

README

Company Valuation

Estimate the intrinsic value of a public company via DCF, relative (peer multiple), and sum-of-parts (SOTP) methods, and blend into a triangulated implied share price with sensitivity tables.

What it does

  • Pulls 5 years of financials + analyst estimates via yfinance
  • Builds a 5-year DCF with explicit revenue / margin / WACC / terminal-value assumptions
  • Applies peer median P/E, EV/Revenue, EV/EBITDA multiples across 4-6 peers
  • Runs SOTP when the company has 2+ distinct reporting segments
  • Presents a blended implied price with method weights, WACC × g sensitivity matrix, and Bull/Base/Bear scenarios
  • Handles banks/REITs/pre-revenue/cyclical edge cases with appropriate fallbacks

Triggers

what is AAPL worth, valuation of NVDA, fair value of TSLA, DCF for MSFT, build a DCF, intrinsic value, implied share price, is X overvalued/undervalued, relative valuation, EV/EBITDA target, SOTP, sum of the parts, price target from fundamentals, value this company

Prerequisites

  • Python 3.8+
  • yfinance, numpy, pandas (auto-installed if missing)

Platform

CLI-based agents (Claude Code). Requires shell + pip.

Setup

No authentication required. First run will auto-install dependencies.

Reference Files

  • references/dcf.md — DCF methodology, industry-specific guidance (software, retail, financials, healthcare, energy, manufacturing, CPG, telecom, REITs, streaming), common pitfalls
  • references/relative_valuation.md — Peer selection heuristics, multiple adjustment rules, Rule of 40 for SaaS, default peer sets by theme
  • references/sotp.md — Sum-of-parts methodology, conglomerate discount detection, catalyst framework, position sizing
  • references/wacc_erp_rates.md — Risk-free rates (live + default), equity risk premiums, sector WACC bands, sector-default betas, terminal growth ceilings

Output

Structured briefing with: headline verdict, snapshot, three-method summary, DCF build, peer comparison, SOTP (if applicable), sensitivity matrix, scenarios, key risks, and caveats.

Disclaimer

For research and educational purposes only. Not financial advice.

Skill manifest

Company Valuation

If your host has not already executed the shell probes below (for example, in Codex), run the commands inside the ! wrappers with its shell tool before choosing a setup path. Use the actual output; if shell access is unavailable, report that limitation.

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

Settle every parameter before pulling data — these defaults apply unless the user overrides them.

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
rev = income_a.loc["Total Revenue"].dropna().iloc[::-1].values  # yfinance columns are newest-first; reverse to oldest -> newest
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())
eff_tax     = (income_a.loc["Tax Provision"] / income_a.loc["Pretax Income"]).iloc[:3].median()
tax_rate    = float(min(0.30, max(0.15, eff_tax))) if pd.notna(eff_tax) else 0.21  # 3y median effective, floored 15%, capped 30%

# 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

Present the valuation in this order:

  1. Headline verdict — one sentence with the blended fair value, the current price, the % upside or downside, and which method is most bullish or bearish.
  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 — which assumptions move the answer most, and 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
Files (finance-skills)
  • references
    • dcf.md 7.3 KB
      Loading…
    • relative_valuation.md 5.5 KB
      Loading…
    • sotp.md 5.5 KB
      Loading…
    • wacc_erp_rates.md 6.7 KB
      Loading…
  • README.md 2.1 KB
    # Company Valuation
    
    Estimate the intrinsic value of a public company via DCF, relative (peer multiple), and sum-of-parts (SOTP) methods, and blend into a triangulated implied share price with sensitivity tables.
    
    ## What it does
    
    - Pulls 5 years of financials + analyst estimates via yfinance
    - Builds a 5-year DCF with explicit revenue / margin / WACC / terminal-value assumptions
    - Applies peer median P/E, EV/Revenue, EV/EBITDA multiples across 4-6 peers
    - Runs SOTP when the company has 2+ distinct reporting segments
    - Presents a blended implied price with method weights, WACC × g sensitivity matrix, and Bull/Base/Bear scenarios
    - Handles banks/REITs/pre-revenue/cyclical edge cases with appropriate fallbacks
    
    ## Triggers
    
    `what is AAPL worth`, `valuation of NVDA`, `fair value of TSLA`, `DCF for MSFT`, `build a DCF`, `intrinsic value`, `implied share price`, `is X overvalued/undervalued`, `relative valuation`, `EV/EBITDA target`, `SOTP`, `sum of the parts`, `price target from fundamentals`, `value this company`
    
    ## Prerequisites
    
    - Python 3.8+
    - `yfinance`, `numpy`, `pandas` (auto-installed if missing)
    
    ## Platform
    
    CLI-based agents (Claude Code). Requires shell + pip.
    
    ## Setup
    
    No authentication required. First run will auto-install dependencies.
    
    ## Reference Files
    
    - `references/dcf.md` — DCF methodology, industry-specific guidance (software, retail, financials, healthcare, energy, manufacturing, CPG, telecom, REITs, streaming), common pitfalls
    - `references/relative_valuation.md` — Peer selection heuristics, multiple adjustment rules, Rule of 40 for SaaS, default peer sets by theme
    - `references/sotp.md` — Sum-of-parts methodology, conglomerate discount detection, catalyst framework, position sizing
    - `references/wacc_erp_rates.md` — Risk-free rates (live + default), equity risk premiums, sector WACC bands, sector-default betas, terminal growth ceilings
    
    ## Output
    
    Structured briefing with: headline verdict, snapshot, three-method summary, DCF build, peer comparison, SOTP (if applicable), sensitivity matrix, scenarios, key risks, and caveats.
    
    ## Disclaimer
    
    For research and educational purposes only. Not financial advice.
    
  • SKILL.md 13.1 KB
    ---
    name: company-valuation
    description: >
      Estimate a public company's intrinsic value with DCF, relative (peer multiple), and
      sum-of-the-parts (SOTP) methods, then blend them into an implied share price with
      upside/downside vs the market price, a WACC and terminal-growth sensitivity grid,
      and bull/base/bear scenarios. Use this skill whenever the user asks what a company
      or ticker is worth: fair value, intrinsic value, implied share price, a price target
      from fundamentals, whether it is overvalued or undervalued, building a DCF (WACC,
      terminal value, discounted cash flow), EV/EBITDA or P/E based targets, peer
      comparison valuation, or SOTP and conglomerate discounts. Run the model rather than
      answering valuation questions from memory.
    ---
    
    # Company Valuation
    
    If your host has not already executed the shell probes below (for example, in Codex), run the commands inside the `!` wrappers with its shell tool before choosing a setup path. Use the actual output; if shell access is unavailable, report that limitation.
    
    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
    
    Settle every parameter before pulling data — these defaults apply unless the user overrides them.
    
    | 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
    
    ```python
    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:
    
    ```python
    # 4a. Revenue growth path — fade from Y1 (consensus or hist CAGR) to terminal g
    rev = income_a.loc["Total Revenue"].dropna().iloc[::-1].values  # yfinance columns are newest-first; reverse to oldest -> newest
    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())
    eff_tax     = (income_a.loc["Tax Provision"] / income_a.loc["Pretax Income"]).iloc[:3].median()
    tax_rate    = float(min(0.30, max(0.15, eff_tax))) if pd.notna(eff_tax) else 0.21  # 3y median effective, floored 15%, capped 30%
    
    # 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`.
    
    ```python
    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
    
    ```python
    # 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
    
    Present the valuation in this order:
    
    1. **Headline verdict** — one sentence with the blended fair value, the current price, the % upside or downside, and which method is most bullish or bearish.
    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** — which assumptions move the answer most, and 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
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.