9.04 GARCH(1,1) model

We reproduce here the figure 9.5 of the book. Utilitary functions can be found next to this file. Here, we only define codpy-related functions.

Necessary Imports

import os
import sys

import matplotlib.pyplot as plt
import numpy as np

from codpy.kernel import Sampler

try:
    CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
except NameError:
    CURRENT_DIR = os.getcwd()
data_path = os.path.join(CURRENT_DIR, "data")
PARENT_DIR = os.path.abspath(os.path.join(CURRENT_DIR, ".."))
sys.path.insert(0, PARENT_DIR)

import utils.ch9.mapping as maps
from utils.ch9.data_utils import stats_df
from utils.ch9.market_data import retrieve_market_data
from utils.ch9.path_generation import generate_paths
from utils.ch9.plot_utils import display_historical_vs_generated_distribution

Parameter definition

def get_cdpres_param():
    return {
        "rescale_kernel": {"max": 2000, "seed": None},
        "rescale": True,
        "grid_projection": True,
        "reproductibility": False,
        "date_format": "%d/%m/%Y",
        "begin_date": "01/06/2020",
        "end_date": "01/06/2022",
        "today_date": "01/06/2022",
        "symbols": ["AAPL", "GOOGL", "AMZN"],
    }

Get the market data

params = retrieve_market_data()

Defining the map

The GARCH(p,q) map is defined as:

\[\begin{split}\begin{aligned} X^k &= \mu + \sigma^k \epsilon^k, \\ (\sigma^k)^2 &= \alpha_0 + \sum_{i=1}^{p} \alpha_i (X^{k-i})^2 + \sum_{j=1}^{q} \beta_j (\sigma^{k-j})^2, \end{aligned}\end{split}\]
where \(\mu \in \mathbb{R}\), \(\{\epsilon^k\}\) is a white noise sequence with unit variance, and \(\sigma^k\) is a stochastic volatility term determined recursively. The parameters \(\alpha_i\) and \(\beta_i\) denote the GARCH parameters.

q = 1


def garch_pq(x):
    import numpy as np
    from arch import arch_model

    p_values = range(1, 6)
    q_values = range(1, 6)
    aic_values = np.full((len(p_values), len(q_values)), np.inf)

    for i in p_values:
        for j in q_values:
            try:
                model = arch_model(x, vol="Garch", p=i, q=j)
                results = model.fit(disp="off")  # suppress convergence messages
                aic_values[i - 1, j - 1] = results.aic
            except:
                continue

    p, q = np.unravel_index(np.argmin(aic_values, axis=None), aic_values.shape)
    print(f"The smallest AIC is {aic_values[p, q]} for model GARCH({p}, {q})")

    best_p, best_q = p + 1, q + 1
    model = arch_model(x, vol="Garch", p=1, q=1)
    results = model.fit()
    print(results.summary())
    a0 = results.params["omega"]
    a = [
        results.params[f"alpha[{i+1}]"]
        for i in range(best_p)
        if f"alpha[{i+1}]" in results.params
    ]
    b = [
        results.params[f"beta[{i+1}]"]
        for i in range(best_q)
        if f"beta[{i+1}]" in results.params
    ]
    return a, b


def estimate_coeff():
    asx, bsx = [], []
    for i in range(params["data"].values.shape[1]):
        ax, bx = garch_pq(params["data"].values[:, i])
        asx += [ax]
        bsx += [bx]
    return np.asarray(asx).T, np.asarray(bsx).T


params["a"], params["b"] = estimate_coeff()
params["q"] = q
params["map"] = maps.composition_map(
    [maps.garch_map(), maps.mean_map(), maps.diff(), maps.log_map, maps.remove_time()]
)
params = maps.apply_map(params)
The smallest AIC is 4141.037702885732 for model GARCH(0, 0)
Iteration:      1,   Func. Count:      6,   Neg. LLF: 16986.629356658286
Iteration:      2,   Func. Count:     12,   Neg. LLF: 2988.1019261412503
Iteration:      3,   Func. Count:     18,   Neg. LLF: 2693.0760787188756
Iteration:      4,   Func. Count:     24,   Neg. LLF: 2100.2759470630394
Iteration:      5,   Func. Count:     30,   Neg. LLF: 2092.166149373335
Iteration:      6,   Func. Count:     36,   Neg. LLF: 2102.752613213072
Iteration:      7,   Func. Count:     42,   Neg. LLF: 2073.109754758848
Iteration:      8,   Func. Count:     47,   Neg. LLF: 2079.0329137844983
Iteration:      9,   Func. Count:     53,   Neg. LLF: 2069.501709914081
Iteration:     10,   Func. Count:     58,   Neg. LLF: 2068.2699961477
Iteration:     11,   Func. Count:     63,   Neg. LLF: 2067.5250950620048
Iteration:     12,   Func. Count:     68,   Neg. LLF: 2066.6828346716716
Iteration:     13,   Func. Count:     73,   Neg. LLF: 2066.542076310764
Iteration:     14,   Func. Count:     78,   Neg. LLF: 2066.5196720137887
Iteration:     15,   Func. Count:     83,   Neg. LLF: 2066.5189598207403
Iteration:     16,   Func. Count:     88,   Neg. LLF: 2066.518854345993
Iteration:     17,   Func. Count:     93,   Neg. LLF: 2066.518851442866
Iteration:     18,   Func. Count:     97,   Neg. LLF: 2066.5188514428664
Optimization terminated successfully    (Exit mode 0)
            Current function value: 2066.518851442866
            Iterations: 18
            Function evaluations: 97
            Gradient evaluations: 18
                     Constant Mean - GARCH Model Results
==============================================================================
Dep. Variable:                      y   R-squared:                       0.000
Mean Model:             Constant Mean   Adj. R-squared:                  0.000
Vol Model:                      GARCH   Log-Likelihood:               -2066.52
Distribution:                  Normal   AIC:                           4141.04
Method:            Maximum Likelihood   BIC:                           4157.94
                                        No. Observations:                  506
Date:              ven., août 22 2025   Df Residuals:                      505
Time:                        18:42:56   Df Model:                            1
                                 Mean Model
============================================================================
                 coef    std err          t      P>|t|      95.0% Conf. Int.
----------------------------------------------------------------------------
mu           125.5477      1.289     97.382      0.000 [1.230e+02,1.281e+02]
                              Volatility Model
===========================================================================
                 coef    std err          t      P>|t|     95.0% Conf. Int.
---------------------------------------------------------------------------
omega          3.7144      1.210      3.069  2.149e-03    [  1.342,  6.087]
alpha[1]       0.9041  9.503e-02      9.514  1.828e-21    [  0.718,  1.090]
beta[1]        0.0950  9.628e-02      0.987      0.324 [-9.370e-02,  0.284]
===========================================================================

Covariance estimator: robust
The smallest AIC is 3535.7251594871695 for model GARCH(0, 0)
Iteration:      1,   Func. Count:      6,   Neg. LLF: 602675894.7026879
Iteration:      2,   Func. Count:     12,   Neg. LLF: 2466.504882261799
Iteration:      3,   Func. Count:     19,   Neg. LLF: 1923.80528227795
Iteration:      4,   Func. Count:     25,   Neg. LLF: 1777.3978356491607
Iteration:      5,   Func. Count:     30,   Neg. LLF: 1770.7938839231458
Iteration:      6,   Func. Count:     35,   Neg. LLF: 1771.654547784799
Iteration:      7,   Func. Count:     41,   Neg. LLF: 1765.428021139962
Iteration:      8,   Func. Count:     46,   Neg. LLF: 1764.8287218853977
Iteration:      9,   Func. Count:     51,   Neg. LLF: 1764.4434332793412
Iteration:     10,   Func. Count:     56,   Neg. LLF: 1764.1555080824837
Iteration:     11,   Func. Count:     61,   Neg. LLF: 1763.8797244685038
Iteration:     12,   Func. Count:     66,   Neg. LLF: 1763.863264750603
Iteration:     13,   Func. Count:     71,   Neg. LLF: 1763.8625911283284
Iteration:     14,   Func. Count:     76,   Neg. LLF: 1763.8625797435848
Iteration:     15,   Func. Count:     80,   Neg. LLF: 1763.8625797422947
Optimization terminated successfully    (Exit mode 0)
            Current function value: 1763.8625797435848
            Iterations: 15
            Function evaluations: 80
            Gradient evaluations: 15
                     Constant Mean - GARCH Model Results
==============================================================================
Dep. Variable:                      y   R-squared:                       0.000
Mean Model:             Constant Mean   Adj. R-squared:                  0.000
Vol Model:                      GARCH   Log-Likelihood:               -1763.86
Distribution:                  Normal   AIC:                           3535.73
Method:            Maximum Likelihood   BIC:                           3552.63
                                        No. Observations:                  506
Date:              ven., août 22 2025   Df Residuals:                      505
Time:                        18:42:57   Df Model:                            1
                                 Mean Model
============================================================================
                 coef    std err          t      P>|t|      95.0% Conf. Int.
----------------------------------------------------------------------------
mu           161.9109      0.790    204.905      0.000 [1.604e+02,1.635e+02]
                               Volatility Model
=============================================================================
                 coef    std err          t      P>|t|       95.0% Conf. Int.
-----------------------------------------------------------------------------
omega          6.3218      1.499      4.218  2.466e-05      [  3.384,  9.259]
alpha[1]       0.9650  5.197e-02     18.568  5.800e-77      [  0.863,  1.067]
beta[1]        0.0350  3.141e-02      1.115      0.265 [-2.654e-02,9.660e-02]
=============================================================================

Covariance estimator: robust
The smallest AIC is 4340.552035447352 for model GARCH(0, 0)
Iteration:      1,   Func. Count:      6,   Neg. LLF: 16275.618037269702
Iteration:      2,   Func. Count:     13,   Neg. LLF: 2488.4649454939345
Iteration:      3,   Func. Count:     19,   Neg. LLF: 2213.4460017426973
Iteration:      4,   Func. Count:     25,   Neg. LLF: 2263.835511756241
Iteration:      5,   Func. Count:     31,   Neg. LLF: 2168.8691710334742
Iteration:      6,   Func. Count:     36,   Neg. LLF: 3108.6099731613394
Iteration:      7,   Func. Count:     42,   Neg. LLF: 2553.7899311915207
Iteration:      8,   Func. Count:     48,   Neg. LLF: 21964.18573516304
Iteration:      9,   Func. Count:     54,   Neg. LLF: 2168.38146506957
Iteration:     10,   Func. Count:     60,   Neg. LLF: 2166.5913578081636
Iteration:     11,   Func. Count:     65,   Neg. LLF: 2166.411205000175
Iteration:     12,   Func. Count:     70,   Neg. LLF: 2166.381080251631
Iteration:     13,   Func. Count:     75,   Neg. LLF: 2166.3639915405465
Iteration:     14,   Func. Count:     80,   Neg. LLF: 2166.3019670466983
Iteration:     15,   Func. Count:     85,   Neg. LLF: 2166.276311352476
Iteration:     16,   Func. Count:     90,   Neg. LLF: 2166.2760295185026
Iteration:     17,   Func. Count:     95,   Neg. LLF: 2166.276017723676
Iteration:     18,   Func. Count:     99,   Neg. LLF: 2166.2760178119124
Optimization terminated successfully    (Exit mode 0)
            Current function value: 2166.276017723676
            Iterations: 18
            Function evaluations: 99
            Gradient evaluations: 18
                     Constant Mean - GARCH Model Results
==============================================================================
Dep. Variable:                      y   R-squared:                       0.000
Mean Model:             Constant Mean   Adj. R-squared:                  0.000
Vol Model:                      GARCH   Log-Likelihood:               -2166.28
Distribution:                  Normal   AIC:                           4340.55
Method:            Maximum Likelihood   BIC:                           4357.46
                                        No. Observations:                  506
Date:              ven., août 22 2025   Df Residuals:                      505
Time:                        18:42:57   Df Model:                            1
                                 Mean Model
============================================================================
                 coef    std err          t      P>|t|      95.0% Conf. Int.
----------------------------------------------------------------------------
mu           114.6062      0.480    238.672      0.000 [1.137e+02,1.155e+02]
                            Volatility Model
========================================================================
                 coef    std err          t      P>|t|  95.0% Conf. Int.
------------------------------------------------------------------------
omega          2.7980      0.926      3.023  2.502e-03 [  0.984,  4.612]
alpha[1]       0.9983      0.149      6.709  1.964e-11 [  0.707,  1.290]
beta[1]    6.5511e-17      0.134  4.879e-16      1.000 [ -0.263,  0.263]
========================================================================

Covariance estimator: robust

We define our sampler on the mapped data using codpy’s Sampler

You can define your own latent generator function, here we use a simple uniform distribution. But if not provided, a default one will be used by the Sampler class.

mapped_data = params["transform_h"].values
generator = lambda n: np.random.uniform(size=(n, mapped_data.shape[1]))
sampler = Sampler(mapped_data, latent_generator=generator)
params["sampler"] = sampler

We plot the original distribution vs the generated one

params = display_historical_vs_generated_distribution(params)
params["graphic"](params)
plt.show()
ch9 garch

Reproductibility test

We regenerate the same path by generating from the latent representation We make sure we get the original data back.

params["reproductibility"] = True
params = generate_paths(params)
params["graphic"](params)
plt.show()
ch9 garch

We now generate a new set of 10 paths

params["reproductibility"] = False
params["Nz"] = 10
params = generate_paths(params)
params["graphic"](params)
plt.show()

stats = stats_df(params["transform_h"], params["transform_g"]).T
print(stats)
ch9 garch
                          0                  1                  2
Mean      -6.7e-06(0.00016)  -1.2e-06(2.7e-05)  -8.4e-06(0.00041)
Variance      -0.068(-0.24)       -0.44(0.076)      -0.089(-0.28)
Skewness    0.0004(0.00039)    0.0005(0.00043)   0.00033(0.00028)
Kurtosis            2(0.31)           6.7(1.1)           1.4(0.6)
KS test          0.54(0.05)         0.64(0.05)         0.72(0.05)

Total running time of the script: (0 minutes 4.835 seconds)

Gallery generated by Sphinx-Gallery