Markets: A Rigorous Course

Exercise · m06_mc_valuation.py

Monte Carlo distribution of intrinsic value; margin of safety as a probability

Exercise stubs. See notes.md 6.7.

Model: the two-stage DCF of m06_dcf.dcf_value with uncertain inputs fcf0 ~ N(mu, sd), growth ~ N(mu, sd), wacc ~ N(mu, sd), terminal_g ~ N(mu, sd), independent, truncated to the region wacc - terminal_g >= min_spread (rejection sampling).

Tests (5)

  • test_vectorised_matches_reference
  • test_degenerate_distribution_is_point_value
  • test_linear_case_matches_closed_form
  • test_convexity_in_discount_rate
  • test_truncation_and_monotonicity

Theory: M06 notes. Run with Ctrl/⌘+Enter.

not run yet

Not saved: sign up to keep your code
"""M06 · Monte Carlo distribution of intrinsic value; margin of safety as a probability.

Exercise stubs. See notes.md §6.7.

Model: the two-stage DCF of m06_dcf.dcf_value with uncertain inputs
    fcf0 ~ N(mu, sd), growth ~ N(mu, sd), wacc ~ N(mu, sd), terminal_g ~ N(mu, sd),
independent, truncated to the region wacc - terminal_g >= min_spread (rejection sampling).
"""

from __future__ import annotations

import numpy as np


def dcf_value_vec(fcf0, growth, wacc, terminal_g, years: int = 5) -> np.ndarray:
    """Vectorised two-stage DCF value (same convention as m06_dcf.dcf_value, value only).

    Inputs are scalars or 1-D arrays that broadcast to a common shape (k,). Return shape (k,)
    (a scalar input returns shape (1,)). No loops over draws: use numpy broadcasting with
    t = 1..years along a second axis.
    """
    raise NotImplementedError


def simulate_intrinsic_values(n: int, seed: int, fcf0=(100.0, 0.0), growth=(0.15, 0.0),
                              wacc=(0.12, 0.0), terminal_g=(0.05, 0.0), years: int = 5,
                              min_spread: float = 0.01) -> np.ndarray:
    """Return exactly n simulated intrinsic values (np.ndarray, shape (n,)).

    Each input argument is a (mean, sd) pair for an independent normal. Use
    np.random.default_rng(seed). Reject draws with wacc - terminal_g < min_spread and draw
    again until n accepted values exist. With all sd = 0 every value equals the point DCF.
    """
    raise NotImplementedError


def prob_value_exceeds_price(values: np.ndarray, price: float) -> float:
    """Monte Carlo estimate of P(V > price) as a float."""
    raise NotImplementedError


def value_quantile(values: np.ndarray, q: float) -> float:
    """q-quantile of the simulated values."""
    raise NotImplementedError


def margin_of_safety(value_estimate: float, price: float) -> float:
    """Graham margin of safety (V - P)/V."""
    raise NotImplementedError

No runs yet

The stub's functions raise NotImplementedError until you write them. Fill in one, press Run tests, and watch its tests turn green.

Reference solution

Available after your first run, with an account approved for code runs.

Create a free account

Reading is open to everyone. An account keeps your progress, answers, notes and code drafts, and gives you the study plan, the journals and the simulator. Running code needs an account approved for it.