Skip to content

archette

Let's build arch-like models from scratch.

This includes model specification, maximum likelihood estimation, etc.

The current implementation is to use numpy for array manipulations, scipy for optimization of objectives, and numba for acceleration.

TODO: reduce dependency to jax alone, with optimization done by hand (gradient descent and the likes) in place of the high level API of scipy.optimize.

API Reference

archette.garch.GARCH

GARCH()

GARCH(1,1) model

Source code in archette/garch.py
def __init__(self):
    self._y = None
    self.params = None  # [omega, alpha, beta]
    self._is_fit = False
    self._v_init = None

params instance-attribute

params = None

vs cached property

vs

conditional variance based on the fit parameters. Must call the fit method first before accessing this property

RETURNS DESCRIPTION
ndarray

estimated conditional variance at each time point

std_resids cached property

std_resids

standardized residual based on the fit parameters. Must call the fit method first before accessing this property

RETURNS DESCRIPTION
ndarray

estimated standardized residual at each time point

fit

fit(y)

fit a GARCH(1,1) model with MLE (assuming Gaussian noise)

PARAMETER DESCRIPTION
y

observed time series

TYPE: ndarray

RETURNS DESCRIPTION
GARCH

self

Source code in archette/garch.py
def fit(self, y: np.ndarray) -> "GARCH":
    """fit a GARCH(1,1) model with MLE (assuming Gaussian noise)

    Parameters:
        y: observed time series

    Returns:
        self
    """
    self._y = y
    self._v_init = (
        y.var()
    )  # != arch_model(y).fit().conditional_volatility[0]**2 but close
    func = self.nll
    self.params = minimize(
        func,
        x0=(self._v_init * 0.4, 0.3, 0.3),
        bounds=[(0, None), (0, None), (0, None)],
        constraints={"type": "ineq", "fun": lambda x: 1 - x[1] - x[2]},
    ).x
    self._is_fit = True
    return self

nll

nll(params)

negative log likelihood of the series at the given params

PARAMETER DESCRIPTION
params

[omega, alpha, beta]

TYPE: ndarray

RETURNS DESCRIPTION
float

negative log likelihood

Source code in archette/garch.py
def nll(self, params: np.ndarray) -> float:
    """negative log likelihood of the series at the given params

    Parameters:
        params: [omega, alpha, beta]

    Returns:
        negative log likelihood
    """
    return _nllgauss(self._y, params, self._v_init)

forecast_vs

forecast_vs(horizon)

forecast conditional variance in the horizon (future)

PARAMETER DESCRIPTION
horizon

forecast horizon

TYPE: int

RETURNS DESCRIPTION
ndarray

forecasted conditional variance

Source code in archette/garch.py
def forecast_vs(self, horizon: int) -> np.ndarray:
    """forecast conditional variance in the horizon (future)

    Parameters:
        horizon: forecast horizon

    Returns:
        forecasted conditional variance
    """
    assert self._is_fit
    return _make_fcst_vs(self.params, self.vs[-1], self._y[-1], horizon)

simulate

simulate(horizon, method='simulate', n_rep=1000, seed=42)

simulate paths from the fitted model

PARAMETER DESCRIPTION
horizon

path length

TYPE: int

method

"bootstrap" or "simulate" (generate new noise)

TYPE: Literal['bootstrap', 'simulate'] DEFAULT: 'simulate'

n_rep

number of repetitions

TYPE: int DEFAULT: 1000

seed

random seed

TYPE: int DEFAULT: 42

RETURNS DESCRIPTION
ndarray

simulated paths

Source code in archette/garch.py
def simulate(
    self,
    horizon: int,
    method: Literal["bootstrap", "simulate"] = "simulate",
    n_rep: int = 1_000,
    seed: int = 42,
) -> np.ndarray:
    """simulate paths from the fitted model


    Parameters:
        horizon: path length
        method: "bootstrap" or "simulate" (generate new noise)
        n_rep: number of repetitions
        seed: random seed

    Returns:
        simulated paths
    """
    assert self._is_fit
    if method == "bootstrap":
        np.random.seed(seed)
        ws = np.random.choice(self.std_resids, size=(n_rep, horizon), replace=True)
    else:
        ws = None
    return _simulate(
        self.params, self._y[-1], self.vs[-1], horizon, n_rep, seed, ws
    )