← Files Bigdata.comARCHIVED FILE

skills/bigdata-variant-perception/scripts/reverse_dcf.py

14.7 KB · Sep 30, 2026 · 23:19 UTC

↓ Download file

#!/usr/bin/env python3
"""
Reverse DCF Model

This module extracts implied market expectations from current stock prices.
Instead of projecting fundamentals to derive a price, it works backwards
from the market price to determine what growth and margin assumptions
are embedded in the current valuation.

Key outputs:
- Implied revenue growth rate
- Implied terminal margin
- Comparison to analyst estimates

Author: Equity Analyst Skill
"""

from typing import Dict, Optional, Tuple
from dataclasses import dataclass


@dataclass
class ReverseDCFInputs:
    """Input parameters for reverse DCF analysis."""
    current_price: float           # Current stock price
    shares_outstanding: float      # Shares outstanding (in millions)
    net_debt: float               # Net debt (debt minus cash)
    wacc: float                   # Weighted average cost of capital
    terminal_growth: float        # Assumed perpetual growth rate
    base_margin: float            # Starting EBITDA margin
    current_revenue: float        # Current annual revenue
    years: int                    # Number of projection years
    tax_rate: float = 0.21        # Corporate tax rate
    capex_pct: float = 0.05       # CapEx as % of revenue
    nwc_pct: float = 0.10         # NWC as % of revenue
    depreciation_pct: float = 0.03  # D&A as % of revenue


@dataclass
class ReverseDCFOutput:
    """Output results from reverse DCF analysis."""
    implied_revenue_growth: float      # Annual revenue growth rate implied by price
    implied_terminal_revenue: float    # Revenue at end of projection period
    implied_terminal_margin: float     # EBITDA margin at terminal year
    implied_fcff_terminal: float       # Terminal year FCFF
    market_implied_ev: float           # Enterprise value from market price
    iterations: int                    # Number of iterations to converge


def calculate_implied_ev(
    price: float,
    shares: float,
    net_debt: float
) -> float:
    """
    Calculate implied enterprise value from market price.

    EV = Market Cap + Net Debt

    Args:
        price: Current stock price
        shares: Shares outstanding
        net_debt: Net debt (positive = debt, negative = net cash)

    Returns:
        Implied enterprise value
    """
    market_cap = price * shares
    return market_cap + net_debt


def calculate_fcff_from_revenue(
    revenue: float,
    ebitda_margin: float,
    tax_rate: float,
    capex_pct: float,
    nwc_change: float,
    depreciation_pct: float
) -> float:
    """
    Calculate FCFF from revenue and margin assumptions.

    Args:
        revenue: Annual revenue
        ebitda_margin: EBITDA margin as decimal
        tax_rate: Tax rate as decimal
        capex_pct: CapEx as % of revenue
        nwc_change: Change in NWC
        depreciation_pct: D&A as % of revenue

    Returns:
        Free cash flow to firm
    """
    ebitda = revenue * ebitda_margin
    depreciation = revenue * depreciation_pct
    ebit = ebitda - depreciation
    nopat = ebit * (1 - tax_rate)
    capex = revenue * capex_pct
    fcff = nopat + depreciation - capex - nwc_change
    return fcff


def project_dcf_value(
    current_revenue: float,
    growth_rate: float,
    margin: float,
    years: int,
    wacc: float,
    terminal_growth: float,
    tax_rate: float,
    capex_pct: float,
    nwc_pct: float,
    depreciation_pct: float
) -> Tuple[float, float]:
    """
    Project DCF value given growth and margin assumptions.

    Args:
        current_revenue: Starting revenue
        growth_rate: Annual revenue growth rate
        margin: EBITDA margin (assumed constant for simplicity)
        years: Projection years
        wacc: Discount rate
        terminal_growth: Perpetual growth rate
        tax_rate: Corporate tax rate
        capex_pct: CapEx as % of revenue
        nwc_pct: NWC as % of revenue
        depreciation_pct: D&A as % of revenue

    Returns:
        Tuple of (enterprise value, terminal year FCFF)
    """
    pv_fcff_sum = 0
    prev_nwc = current_revenue * nwc_pct
    revenue = current_revenue
    terminal_fcff = 0

    for year in range(1, years + 1):
        revenue = revenue * (1 + growth_rate)
        current_nwc = revenue * nwc_pct
        nwc_change = current_nwc - prev_nwc
        prev_nwc = current_nwc

        fcff = calculate_fcff_from_revenue(
            revenue=revenue,
            ebitda_margin=margin,
            tax_rate=tax_rate,
            capex_pct=capex_pct,
            nwc_change=nwc_change,
            depreciation_pct=depreciation_pct
        )

        pv_fcff = fcff / ((1 + wacc) ** year)
        pv_fcff_sum += pv_fcff
        terminal_fcff = fcff

    # Terminal value
    if wacc > terminal_growth:
        terminal_value = terminal_fcff * (1 + terminal_growth) / (wacc - terminal_growth)
        pv_terminal = terminal_value / ((1 + wacc) ** years)
    else:
        pv_terminal = 0

    enterprise_value = pv_fcff_sum + pv_terminal
    return enterprise_value, terminal_fcff


def solve_implied_growth(
    inputs: ReverseDCFInputs,
    tolerance: float = 0.001,
    max_iterations: int = 100
) -> ReverseDCFOutput:
    """
    Solve for implied revenue growth rate using binary search.

    This function iteratively finds the growth rate that, when used
    in a standard DCF model, produces an enterprise value equal to
    the market-implied enterprise value.

    Args:
        inputs: ReverseDCFInputs with market data and assumptions
        tolerance: Convergence tolerance (as % of target EV)
        max_iterations: Maximum iterations before giving up

    Returns:
        ReverseDCFOutput with implied growth and metrics
    """
    target_ev = calculate_implied_ev(
        inputs.current_price,
        inputs.shares_outstanding,
        inputs.net_debt
    )

    # Binary search bounds
    low_growth = -0.20  # -20% annual decline
    high_growth = 0.50  # 50% annual growth

    iterations = 0
    implied_growth = 0
    terminal_fcff = 0

    while iterations < max_iterations:
        iterations += 1
        mid_growth = (low_growth + high_growth) / 2

        ev, terminal_fcff = project_dcf_value(
            current_revenue=inputs.current_revenue,
            growth_rate=mid_growth,
            margin=inputs.base_margin,
            years=inputs.years,
            wacc=inputs.wacc,
            terminal_growth=inputs.terminal_growth,
            tax_rate=inputs.tax_rate,
            capex_pct=inputs.capex_pct,
            nwc_pct=inputs.nwc_pct,
            depreciation_pct=inputs.depreciation_pct
        )

        error_pct = abs(ev - target_ev) / target_ev

        if error_pct < tolerance:
            implied_growth = mid_growth
            break

        if ev < target_ev:
            low_growth = mid_growth
        else:
            high_growth = mid_growth

        implied_growth = mid_growth

    # Calculate terminal revenue
    terminal_revenue = inputs.current_revenue * ((1 + implied_growth) ** inputs.years)

    return ReverseDCFOutput(
        implied_revenue_growth=implied_growth,
        implied_terminal_revenue=terminal_revenue,
        implied_terminal_margin=inputs.base_margin,
        implied_fcff_terminal=terminal_fcff,
        market_implied_ev=target_ev,
        iterations=iterations
    )


def solve_implied_margin(
    inputs: ReverseDCFInputs,
    assumed_growth: float,
    tolerance: float = 0.001,
    max_iterations: int = 100
) -> float:
    """
    Solve for implied terminal margin given a fixed growth rate.

    Args:
        inputs: ReverseDCFInputs with market data
        assumed_growth: Assumed revenue growth rate
        tolerance: Convergence tolerance
        max_iterations: Maximum iterations

    Returns:
        Implied EBITDA margin
    """
    target_ev = calculate_implied_ev(
        inputs.current_price,
        inputs.shares_outstanding,
        inputs.net_debt
    )

    low_margin = 0.01
    high_margin = 0.60

    for _ in range(max_iterations):
        mid_margin = (low_margin + high_margin) / 2

        ev, _ = project_dcf_value(
            current_revenue=inputs.current_revenue,
            growth_rate=assumed_growth,
            margin=mid_margin,
            years=inputs.years,
            wacc=inputs.wacc,
            terminal_growth=inputs.terminal_growth,
            tax_rate=inputs.tax_rate,
            capex_pct=inputs.capex_pct,
            nwc_pct=inputs.nwc_pct,
            depreciation_pct=inputs.depreciation_pct
        )

        error_pct = abs(ev - target_ev) / target_ev

        if error_pct < tolerance:
            return mid_margin

        if ev < target_ev:
            low_margin = mid_margin
        else:
            high_margin = mid_margin

    return (low_margin + high_margin) / 2


def compare_to_estimates(
    implied_growth: float,
    implied_margin: float,
    analyst_growth: float,
    analyst_margin: float
) -> Dict[str, any]:
    """
    Compare implied expectations to analyst estimates.

    Args:
        implied_growth: Market-implied growth rate
        implied_margin: Market-implied margin
        analyst_growth: Analyst consensus growth estimate
        analyst_margin: Analyst margin estimate

    Returns:
        Dictionary with comparison metrics
    """
    growth_gap = implied_growth - analyst_growth
    margin_gap = implied_margin - analyst_margin

    # Determine if market is more optimistic or pessimistic
    if growth_gap > 0.02:
        growth_view = "Market expects HIGHER growth than analysts"
    elif growth_gap < -0.02:
        growth_view = "Market expects LOWER growth than analysts"
    else:
        growth_view = "Market roughly aligned with analyst growth estimates"

    if margin_gap > 0.02:
        margin_view = "Market expects HIGHER margins than analysts"
    elif margin_gap < -0.02:
        margin_view = "Market expects LOWER margins than analysts"
    else:
        margin_view = "Market roughly aligned with analyst margin estimates"

    return {
        'implied_growth': implied_growth,
        'analyst_growth': analyst_growth,
        'growth_gap': growth_gap,
        'growth_interpretation': growth_view,
        'implied_margin': implied_margin,
        'analyst_margin': analyst_margin,
        'margin_gap': margin_gap,
        'margin_interpretation': margin_view
    }


def format_reverse_dcf_report(
    output: ReverseDCFOutput,
    inputs: ReverseDCFInputs,
    comparison: Optional[Dict] = None
) -> str:
    """
    Format reverse DCF results as a readable report.

    Args:
        output: ReverseDCFOutput results
        inputs: Original inputs
        comparison: Optional comparison dict from compare_to_estimates

    Returns:
        Formatted string report
    """
    lines = []
    lines.append("=" * 60)
    lines.append("REVERSE DCF ANALYSIS")
    lines.append("=" * 60)

    lines.append("\nMARKET DATA:")
    lines.append(f"  Current Price: ${inputs.current_price:.2f}")
    lines.append(f"  Shares Outstanding: {inputs.shares_outstanding:.1f}M")
    lines.append(f"  Market Cap: ${inputs.current_price * inputs.shares_outstanding:,.0f}M")
    lines.append(f"  Net Debt: ${inputs.net_debt:,.0f}M")
    lines.append(f"  Market-Implied EV: ${output.market_implied_ev:,.0f}M")

    lines.append("\nASSUMPTIONS:")
    lines.append(f"  WACC: {inputs.wacc:.1%}")
    lines.append(f"  Terminal Growth: {inputs.terminal_growth:.1%}")
    lines.append(f"  Projection Years: {inputs.years}")
    lines.append(f"  Base EBITDA Margin: {inputs.base_margin:.1%}")

    lines.append("\nIMPLIED EXPECTATIONS:")
    lines.append(f"  Implied Revenue Growth: {output.implied_revenue_growth:.1%} per year")
    lines.append(f"  Current Revenue: ${inputs.current_revenue:,.0f}M")
    lines.append(f"  Implied Terminal Revenue: ${output.implied_terminal_revenue:,.0f}M")
    lines.append(f"  Revenue Multiple: {output.implied_terminal_revenue / inputs.current_revenue:.1f}x")

    if comparison:
        lines.append("\nCOMPARISON TO ESTIMATES:")
        lines.append(f"  Analyst Growth Estimate: {comparison['analyst_growth']:.1%}")
        lines.append(f"  Growth Gap: {comparison['growth_gap']:+.1%}")
        lines.append(f"  --> {comparison['growth_interpretation']}")
        lines.append(f"  Analyst Margin Estimate: {comparison['analyst_margin']:.1%}")
        lines.append(f"  Margin Gap: {comparison['margin_gap']:+.1%}")
        lines.append(f"  --> {comparison['margin_interpretation']}")

    lines.append("\n" + "=" * 60)
    return "\n".join(lines)


if __name__ == "__main__":
    # Example: Analyze a growth stock's implied expectations
    print("=" * 70)
    print("REVERSE DCF ANALYSIS - EXAMPLE")
    print("=" * 70)

    # Example company trading at $150/share
    inputs = ReverseDCFInputs(
        current_price=150.00,
        shares_outstanding=500,      # 500M shares
        net_debt=-2000,              # $2B net cash (negative debt)
        wacc=0.10,                   # 10% WACC
        terminal_growth=0.03,        # 3% perpetual growth
        base_margin=0.30,            # 30% EBITDA margin
        current_revenue=30000,       # $30B current revenue
        years=10,                    # 10-year DCF
        tax_rate=0.21,
        capex_pct=0.04,
        nwc_pct=0.08
    )

    # Solve for implied growth
    print("\n1. SOLVING FOR IMPLIED GROWTH RATE")
    print("-" * 40)
    result = solve_implied_growth(inputs)

    print(f"\nMarket-Implied Enterprise Value: ${result.market_implied_ev:,.0f}M")
    print(f"Implied Annual Revenue Growth: {result.implied_revenue_growth:.1%}")
    print(f"Terminal Revenue (Year {inputs.years}): ${result.implied_terminal_revenue:,.0f}M")
    print(f"Iterations to converge: {result.iterations}")

    # Now solve for implied margin at different growth assumptions
    print("\n" + "=" * 70)
    print("2. IMPLIED MARGIN AT DIFFERENT GROWTH RATES")
    print("-" * 40)

    growth_scenarios = [0.05, 0.10, 0.15, 0.20]
    for growth in growth_scenarios:
        implied_margin = solve_implied_margin(inputs, assumed_growth=growth)
        print(f"  At {growth:.0%} growth --> Implied margin: {implied_margin:.1%}")

    # Compare to analyst estimates
    print("\n" + "=" * 70)
    print("3. COMPARISON TO ANALYST ESTIMATES")
    print("-" * 40)

    # Hypothetical analyst estimates
    analyst_growth = 0.12  # 12% consensus growth
    analyst_margin = 0.32  # 32% margin target

    comparison = compare_to_estimates(
        implied_growth=result.implied_revenue_growth,
        implied_margin=inputs.base_margin,
        analyst_growth=analyst_growth,
        analyst_margin=analyst_margin
    )

    print(f"\nImplied Growth: {comparison['implied_growth']:.1%}")
    print(f"Analyst Growth: {comparison['analyst_growth']:.1%}")
    print(f"Gap: {comparison['growth_gap']:+.1%}")
    print(f"--> {comparison['growth_interpretation']}")

    # Full formatted report
    print("\n" + "=" * 70)
    print("4. FULL REPORT")
    print("-" * 40)
    print(format_reverse_dcf_report(result, inputs, comparison))

SHA-256: b2aa961443867787b30e795532ee1f86cdbf78472cd69d5513b2f2129b8e1646