"""DCF valuation engine — Gordon Growth Model.""" import numpy as np import pandas as pd def run_dcf( fcf_series: pd.Series, shares_outstanding: float, wacc: float = 0.10, terminal_growth: float = 0.03, projection_years: int = 5, ) -> dict: """ Run a DCF model and return per-year breakdown plus intrinsic value per share. Args: fcf_series: Annual FCF values, most recent first (yfinance order). shares_outstanding: Diluted shares outstanding. wacc: Weighted average cost of capital (decimal, e.g. 0.10). terminal_growth: Perpetuity growth rate (decimal, e.g. 0.03). projection_years: Number of years to project FCFs. Returns: dict with keys: intrinsic_value_per_share, total_pv, terminal_value_pv, fcf_pv_sum, years, projected_fcfs, discounted_fcfs, growth_rate_used """ if fcf_series.empty or shares_outstanding <= 0: return {} # Use last N years of FCF (sorted oldest → newest) historical = fcf_series.sort_index().dropna().values if len(historical) < 2: return {} # Compute average YoY growth rate from historical FCF growth_rates = [] for i in range(1, len(historical)): if historical[i - 1] != 0: g = (historical[i] - historical[i - 1]) / abs(historical[i - 1]) growth_rates.append(g) # Cap growth rate to reasonable bounds [-0.5, 0.5] raw_growth = float(np.median(growth_rates)) if growth_rates else 0.05 growth_rate = max(-0.50, min(0.50, raw_growth)) base_fcf = float(historical[-1]) # most recent FCF # Project FCFs projected_fcfs = [] for year in range(1, projection_years + 1): fcf = base_fcf * ((1 + growth_rate) ** year) projected_fcfs.append(fcf) # Discount projected FCFs discounted_fcfs = [] for i, fcf in enumerate(projected_fcfs, start=1): pv = fcf / ((1 + wacc) ** i) discounted_fcfs.append(pv) fcf_pv_sum = sum(discounted_fcfs) # Terminal value (Gordon Growth Model) terminal_fcf = projected_fcfs[-1] * (1 + terminal_growth) terminal_value = terminal_fcf / (wacc - terminal_growth) terminal_value_pv = terminal_value / ((1 + wacc) ** projection_years) total_pv = fcf_pv_sum + terminal_value_pv intrinsic_value_per_share = total_pv / shares_outstanding return { "intrinsic_value_per_share": intrinsic_value_per_share, "total_pv": total_pv, "terminal_value_pv": terminal_value_pv, "fcf_pv_sum": fcf_pv_sum, "years": list(range(1, projection_years + 1)), "projected_fcfs": projected_fcfs, "discounted_fcfs": discounted_fcfs, "growth_rate_used": growth_rate, "base_fcf": base_fcf, }