1import numpy as np2import pytest34from wp46.models.arch_models import ArchModel5from wp46.models.custom import GARCH116from wp46.models.distributions import loglik_z7from wp46.models.extensions import (EXTENSIONS, BetaTEGARCH11, FIAPARCH11, FIEGARCH11, GARCHMIDAS, HYGARCH11, RealEGARCH11,8 STGARCH11, figarch_weights, frac_diff_coefs)9from tests.unit.test_custom_models import R101112def test_frac_diff_coefficients():13 pi = frac_diff_coefs(0.4, 5)14 assert np.allclose(pi, [1, -0.4, -0.12, -0.064, -0.0416]) # (1-L)^d binomial expansion15 lam = figarch_weights(0.4, 0.0, 0.0, 1.0, 10)16 assert np.allclose(lam, -pi[1:10] if False else -frac_diff_coefs(0.4, 11)[1:11]) # phi = beta = 0: lambda_k = -pi_k171819def test_hygarch_amp1_matches_arch_figarch_likelihood():20 """HYGARCH with amp = 1 is FIGARCH(1,d,1): evaluating our likelihood at arch's optimum (same backcast) reproduces arch's."""21 fa = ArchModel("figarch11").fit(R, dist="t")22 d = dict(zip(fa.param_names, fa.params))23 m = HYGARCH11()24 s0 = m.backcast(R - d["mu"])25 v = np.array([d["omega"], d["d"], d["phi"], d["beta"], 1.0])26 s2 = m.sigma2(v, R - d["mu"], None, s0, 0.5)27 ll = float(np.sum(loglik_z("t", (R - d["mu"]) / np.sqrt(s2), np.array([d["nu"]])) - 0.5 * np.log(s2)))28 assert abs(ll - fa.loglik) < 5.0, (ll, fa.loglik) # same ARCH(inf) truncation; tiny differences from arch's bounds/backcast29 f = m.fit(R, dist="t")30 assert f.converged and f.loglik >= fa.loglik - 1.0 # HYGARCH nests FIGARCH313233def test_fiaparch_nests_figarch_and_stgarch_nests_garch():34 fa = ArchModel("figarch11").fit(R, dist="t")35 f = FIAPARCH11().fit(R, dist="t")36 assert f.converged and f.loglik >= fa.loglik - 1.037 g = GARCH11().fit(R, dist="t")38 s = STGARCH11().fit(R, dist="t")39 assert s.converged and s.admissible and s.loglik >= g.loglik - 1.0404142def test_garch_midas_nests_garch_with_calendar_months():43 g = GARCH11().fit(R, dist="normal")44 months = np.arange(len(R)) // 2145 f = GARCHMIDAS().fit(R, dist="normal", months=months)46 assert f.converged and f.loglik >= g.loglik - 2.047 fc = GARCHMIDAS().forecast(f, R, None, 22)48 assert np.all(fc.step_var > 0) and np.isfinite(fc.step_var).all()495051def test_betategarch_fiegarch_realegarch_fit_and_forecast():52 rng = np.random.default_rng(5)53 x = (R - R.mean()) ** 2 * rng.lognormal(0, 0.3, len(R)) + 0.0554 for cls, xx, dist in ((BetaTEGARCH11, None, "t"), (FIEGARCH11, None, "t"), (RealEGARCH11, x, "normal")):55 f = cls().fit(R, xx, dist=dist)56 assert f.converged, cls.model_id57 fc = cls().forecast(f, R, xx, 22, seed=3)58 assert np.all(fc.step_var > 0) and np.isfinite(fc.step_var).all(), cls.model_id59 fc2 = cls().forecast(f, R, xx, 22, seed=3)60 assert np.array_equal(fc.step_var, fc2.step_var) # seeded simulation616263def test_no_lookahead_extensions():64 for mid in ("stgarch11", "hygarch11", "fiegarch11"):65 m = EXTENSIONS[mid]()66 f = m.fit(R[:2000], dist="t")67 a = m.forecast(f, R[:2000], None, 5, seed=1).step_var68 R2 = R.copy(); R2[2000:] *= 5.069 b = m.forecast(f, R2[:2000], None, 5, seed=1).step_var70 assert np.array_equal(a, b)71