Exercise · m12_scenarios.py
Bayesian scenario updating, expected value and decision trees
not run yet
Not saved: sign up to keep your code"""M12 exercise: Bayesian scenario updating, expected value and decision trees.
Read notes.md §12.5 first. Run:
cd exercises && .venv/bin/python -m pytest modules/M12-geopolitics-events/tests/test_m12_scenarios.py
Scenarios are indexed s = 0..S-1. A prior is a length-S probability vector.
A likelihood vector L has L[s] = P(news | scenario s).
"""
from __future__ import annotations
import numpy as np
def normalize(w) -> np.ndarray:
"""Scale non-negative weights to sum to one. Raise ValueError on negative
weights or a zero sum."""
raise NotImplementedError
def bayes_update(prior, likelihood) -> np.ndarray:
"""Posterior P(s|news) = L[s] prior[s] / sum_j L[j] prior[j] (eq. 12.16).
Normalise the prior first. Raise ValueError if lengths differ."""
raise NotImplementedError
def posterior_odds(prior_odds: float, likelihood_ratio: float) -> float:
"""Odds form (eq. 12.17): posterior odds = prior odds x likelihood ratio."""
raise NotImplementedError
def odds_to_prob(odds: float) -> float:
"""p = odds / (1 + odds)."""
raise NotImplementedError
def sequential_update(prior, likelihoods) -> np.ndarray:
"""Apply likelihood vectors one after another (conditionally independent news).
Return array (k+1, S): row 0 = normalised prior, row j = posterior after j items."""
raise NotImplementedError
def log_odds_update(prior, likelihoods, ref: int = 0) -> np.ndarray:
"""Same final posterior as sequential_update, computed in log-odds against
scenario `ref` (eq. 12.18): log(p_s/p_ref) += log(L_s/L_ref) for each item.
Return probabilities (normalise in a numerically stable way)."""
raise NotImplementedError
def expected_value(probs, payoffs):
"""E[payoff] per action: normalise(probs) @ payoffs, payoffs shape (S,) or (S, A)."""
raise NotImplementedError
def best_action(probs, payoffs) -> tuple[int, np.ndarray]:
"""(argmax index, array of expected values) for payoffs of shape (S, A)."""
raise NotImplementedError
def value_of_perfect_information(probs, payoffs) -> float:
"""EVPI = E_s[max_a payoff(s,a)] - max_a E_s[payoff(s,a)] (eq. 12.20)."""
raise NotImplementedError
def call_payoff(s_T, strike: float) -> np.ndarray:
"""max(S_T - K, 0), elementwise."""
raise NotImplementedError
def put_payoff(s_T, strike: float) -> np.ndarray:
"""max(K - S_T, 0), elementwise."""
raise NotImplementedError
def scenario_pnl(s0: float, s_T, qty: float = 0.0, calls=(), puts=()) -> np.ndarray:
"""Horizon P&L of qty units of the underlying bought at s0 plus option legs.
calls / puts: iterables of (strike, premium, quantity), quantity < 0 = short.
P&L = qty (S_T - s0) + sum over legs of quantity * (payoff - premium)."""
raise NotImplementedError
def tree_value(node):
"""Roll back a decision tree (eq. 12.19). Node formats:
{"type": "terminal", "value": v}
{"type": "chance", "branches": [(prob, child), ...]} (raise ValueError if probs don't sum to 1)
{"type": "decision", "options": {name: child, ...}}
Chance node value = sum prob * child value; decision node value = max over options.
Return (value, policy): policy is a list of (path, chosen_option) for every
decision node, where path is the tuple of labels from the root (chance branches
by integer index, decision branches by option name)."""
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.