Source code for extremeloss.estimation.conditional_mc

from __future__ import annotations

import math

import numpy as np
from scipy.stats import norm

from ..results import TailEstimateResult
from ..utils.validation import validate_alpha, validate_probabilities, validate_threshold, validate_q


def _normal_ci(estimate: float, stderr: float, alpha: float) -> tuple[float, float]:
    z = float(norm.ppf(1.0 - alpha / 2.0))
    return float(estimate - z * stderr), float(estimate + z * stderr)


[docs] def estimate_tail_probability_cmc( conditional_probabilities, *, threshold: float | None = None, alpha: float = 0.05, ) -> TailEstimateResult: """Estimate an exceedance probability from conditional exceedance probabilities. Parameters ---------- conditional_probabilities: Samples of P(X > u | Y) from a conditioning variable Y. threshold: Optional tail threshold associated with the conditional probabilities. """ validate_alpha(alpha) if threshold is not None: validate_threshold(threshold) probs = validate_probabilities(conditional_probabilities, name="conditional_probabilities") estimate = float(np.mean(probs)) stderr = float(np.std(probs, ddof=0) / math.sqrt(probs.size)) return TailEstimateResult( estimate=estimate, method="conditional_monte_carlo", stderr=stderr, ci=_normal_ci(estimate, stderr, alpha), n=int(probs.size), threshold=float(threshold) if threshold is not None else None, diagnostics={ "min_conditional_probability": float(np.min(probs)), "max_conditional_probability": float(np.max(probs)), }, )
[docs] def estimate_tvar_cmc( conditional_tail_expectations, *, q: float, threshold: float | None = None, alpha: float = 0.05, ) -> TailEstimateResult: """Estimate TVaR from conditional expectations of tail losses. `conditional_tail_expectations` should contain draws of E[X | X >= VaR_q, Y] or another conditionally unbiased TVaR contribution. """ validate_q(q) validate_alpha(alpha) if threshold is not None: validate_threshold(threshold) vals = np.asarray(conditional_tail_expectations, dtype=float) if vals.ndim != 1 or vals.size == 0: raise ValueError("conditional_tail_expectations must be a non-empty 1D array") estimate = float(np.mean(vals)) stderr = float(np.std(vals, ddof=0) / math.sqrt(vals.size)) return TailEstimateResult( estimate=estimate, method="conditional_monte_carlo", stderr=stderr, ci=_normal_ci(estimate, stderr, alpha), n=int(vals.size), threshold=float(threshold) if threshold is not None else None, quantile=float(q), diagnostics={ "min_conditional_expectation": float(np.min(vals)), "max_conditional_expectation": float(np.max(vals)), }, )