Source code for jump_diffusion.distributions.student_t

"""
Student-t jump distribution.

Location-scale Student-t, standardized so that ``jump_scale`` is the actual
standard deviation of the jump (matching the convention used elsewhere in
this package -- e.g. ``SGEDJump`` -- rather than scipy's raw ``t`` scale
parameter, whose relationship to the standard deviation depends on the
degrees of freedom). This is the same "standardized Student-t"
parameterization used for Student-t innovations in GARCH-t models
(Bollerslev, 1987): ``jump_df`` alone controls tail fatness, independent of
overall spread.

No closed form is known for the convolution of Student-t with an
independent normal, so this distribution relies on the generic
FFT-convolution and inverse-CDF fallbacks defined in ``JumpDistribution``.
"""

from typing import Dict, Optional, Tuple, cast

import numpy as np
from scipy.stats import t

from .base import JumpDistribution


def _scipy_scale(scale: float, df: float) -> float:
    """Convert the standardized (std-dev) scale to scipy's raw ``t`` scale."""
    return scale * np.sqrt((df - 2) / df)


[docs] class StudentTJump(JumpDistribution): """Jump sizes distributed as a (standardized) Student-t.""" param_names: Tuple[str, ...] = ("jump_loc", "jump_scale", "jump_df")
[docs] def default_params(self) -> Dict[str, float]: return {"jump_loc": 0.0, "jump_scale": 0.15, "jump_df": 5.0}
[docs] def pdf(self, x: np.ndarray, params: Dict[str, float]) -> np.ndarray: df = params["jump_df"] return cast( np.ndarray, t.pdf( x, df=df, loc=params["jump_loc"], scale=_scipy_scale(params["jump_scale"], df), ), )
[docs] def param_bounds(self) -> Dict[str, Tuple[Optional[float], Optional[float]]]: return { "jump_loc": (None, None), "jump_scale": (1e-6, None), "jump_df": (2.05, 100.0), }
[docs] def initial_guess( self, mean_increment: float, std_increment: float, skewness: float, ) -> Dict[str, float]: return { "jump_loc": float(np.sign(skewness)) * std_increment * 0.1, "jump_scale": std_increment * 0.5, "jump_df": 5.0, }
[docs] def rvs( self, params: Dict[str, float], size: int, random_state: Optional[np.random.Generator] = None, ) -> np.ndarray: df = params["jump_df"] return cast( np.ndarray, t.rvs( df=df, loc=params["jump_loc"], scale=_scipy_scale(params["jump_scale"], df), size=size, random_state=random_state, ), )