Source code for merton.calibration.jmr_iterative

"""Jones-Mason-Rosenfeld (1984) iterative calibration.

Solve the two simultaneous equations

::

    E    = A · Φ(d₁) - D · e^{-rT} · Φ(d₂)              (BSM call value)
    σ_E  = (A / E) · Φ(d₁) · σ_A                         (Ito's lemma)

for the unknowns ``A`` (asset value) and ``σ_A`` (asset volatility).
Practically identical to the proprietary Crosbie-Bohn KMV iteration when
applied to a snapshot of one firm.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from ..exceptions import MertonInputError
from ._solvers import solve_two_equation
from .base import CalibrationResult, Calibrator

if TYPE_CHECKING:
    from ..core.firm import Firm


[docs] def jmr_iterative( *, equity: float, equity_vol: float, debt: float, rf: float, T: float, dividend_yield: float = 0.0, tol: float = 1e-8, max_iter: int = 200, ) -> CalibrationResult: """Solve the JMR two-equation system. Scalar inputs only. Examples -------- >>> res = jmr_iterative(equity=80, equity_vol=0.30, debt=60, rf=0.04, T=1.0) >>> abs(res.asset_value - 137) < 5 # rough sanity, exact value depends on σ_E True """ a, sa, n_iter, converged = solve_two_equation( E=float(equity), sigma_E=float(equity_vol), D=float(debt), r=float(rf), T=float(T), q=float(dividend_yield), tol=tol, max_iter=max_iter, ) return CalibrationResult( asset_value=a, asset_vol=sa, n_iter=n_iter, converged=converged, method="jmr_iterative", diagnostics={"tol": tol, "max_iter": max_iter}, )
[docs] class JMRCalibrator(Calibrator): """OO wrapper around :func:`jmr_iterative`."""
[docs] method = "jmr_iterative"
def __init__(self, *, tol: float = 1e-8, max_iter: int = 200) -> None:
[docs] self.tol = tol
[docs] self.max_iter = max_iter
[docs] def fit(self, firm: Firm) -> CalibrationResult: if firm.equity_vol is None: raise MertonInputError( "JMR iterative calibration requires equity_vol", suggested_fix="Pass equity_vol explicitly on the Firm.", ) return jmr_iterative( equity=float(firm.equity), equity_vol=float(firm.equity_vol), debt=float(firm.default_point_value()), rf=float(firm.rf), T=float(firm.horizon), dividend_yield=float(firm.dividend_yield), tol=self.tol, max_iter=self.max_iter, )
__all__ = ["JMRCalibrator", "jmr_iterative"]