Skip to content

Commit 11d18ab

Browse files
committed
Fix arch_model vol check and expand FIAPARCH reduced-order tests
Fix bug using vol_model instead of vol in arch_model type check. Add tests for FIAPARCH with reduced p, o, q combinations including forecasting, simulation, parameter names, and backcast_transform. Made-with: Cursor
1 parent 50b715f commit 11d18ab

4 files changed

Lines changed: 153 additions & 1 deletion

File tree

arch/tests/univariate/test_mean.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,18 @@ def test_fiaparch_non_int_p():
13631363
arch_model(SP500, vol="fiaparch", p=[1, 2])
13641364

13651365

1366+
def test_fiaparch_arch_model_kwargs():
1367+
am = arch_model(SP500, vol="fiaparch", o=0)
1368+
assert isinstance(am.volatility, FIAPARCH)
1369+
assert am.volatility.o == 0
1370+
assert am.volatility.name == "FI Power ARCH"
1371+
1372+
am2 = arch_model(SP500, vol="FIAPARCH", p=0, q=0)
1373+
assert isinstance(am2.volatility, FIAPARCH)
1374+
assert am2.volatility.p == 0
1375+
assert am2.volatility.q == 0
1376+
1377+
13661378
def test_param_cov():
13671379
mod = ConstantMean(SP500)
13681380
res = mod.fit(disp="off")

arch/tests/univariate/test_variance_forecasting.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2010,6 +2010,62 @@ def test_fiaparch_simulation_smoke(self, o, delta):
20102010
method="analytic",
20112011
)
20122012

2013+
@pytest.mark.parametrize("p,q", [(0, 1), (1, 0), (0, 0)])
2014+
def test_fiaparch_one_step_reduced(self, p, q):
2015+
trunc = 50
2016+
vol = FIAPARCH(p=p, q=q, truncation=trunc)
2017+
resids = self.resid
2018+
backcast = vol.backcast(resids)
2019+
var_bounds = vol.variance_bounds(resids)
2020+
params = [0.1]
2021+
if p:
2022+
params.append(0.2)
2023+
params.append(0.4)
2024+
if q:
2025+
params.append(0.2)
2026+
params.extend([-0.3, 1.5])
2027+
params = np.array(params)
2028+
sigma2 = np.empty_like(resids)
2029+
vol.compute_variance(params, resids, sigma2, backcast, var_bounds)
2030+
forecast = vol.forecast(
2031+
params, resids, backcast, var_bounds, horizon=1, start=0
2032+
)
2033+
assert_allclose(sigma2[1:], forecast.forecasts[:-1, 0])
2034+
2035+
@pytest.mark.parametrize("p,q", [(0, 1), (1, 0), (0, 0)])
2036+
def test_fiaparch_simulation_reduced(self, p, q):
2037+
dist = Normal(seed=self.rng)
2038+
rng = dist.simulate([])
2039+
trunc = 50
2040+
vol = FIAPARCH(p=p, q=q, truncation=trunc)
2041+
resids = self.resid
2042+
backcast = vol.backcast(resids)
2043+
var_bounds = vol.variance_bounds(resids)
2044+
params = [0.1]
2045+
if p:
2046+
params.append(0.2)
2047+
params.append(0.4)
2048+
if q:
2049+
params.append(0.2)
2050+
params.extend([-0.3, 1.5])
2051+
params = np.array(params)
2052+
sigma2 = np.empty_like(resids)
2053+
vol.compute_variance(params, resids, sigma2, backcast, var_bounds)
2054+
forecast = vol.forecast(
2055+
params,
2056+
resids,
2057+
backcast,
2058+
var_bounds,
2059+
horizon=10,
2060+
start=0,
2061+
method="simulation",
2062+
rng=rng,
2063+
simulations=100,
2064+
)
2065+
assert_allclose(sigma2[1:], forecast.forecasts[:-1, 0])
2066+
assert forecast.forecast_paths is not None
2067+
assert forecast.shocks is not None
2068+
20132069
def test_midas_analytical(self):
20142070
vol = MIDASHyperbolic()
20152071
resids = self.resid

arch/tests/univariate/test_volatility.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,11 +1979,23 @@ def test_fiaparch_no_phi(setup):
19791979
# omega, d, beta, gamma, delta
19801980
assert len(bounds) == fiaparch.num_params
19811981

1982+
names = fiaparch.parameter_names()
1983+
assert "phi" not in names
1984+
assert names == ["omega", "d", "beta", "gamma", "delta"]
1985+
19821986
a, _ = fiaparch.constraints()
19831987
# No phi => FIGARCH block shrinks (5 rows), gamma (2 rows), delta (2 rows) = 9
19841988
assert a.shape[1] == 5
19851989
assert a.shape[0] == 9
19861990

1991+
backcast = fiaparch.backcast(setup.resids)
1992+
var_bounds = fiaparch.variance_bounds(setup.resids)
1993+
# omega, d, beta, gamma, delta (no phi)
1994+
parameters = np.array([1.0, 0.4, 0.2, -0.3, 1.5])
1995+
sigma2 = np.zeros_like(setup.sigma2)
1996+
fiaparch.compute_variance(parameters, setup.resids, sigma2, backcast, var_bounds)
1997+
assert np.all(np.isfinite(sigma2))
1998+
19871999

19882000
def test_fiaparch_no_beta(setup):
19892001
fiaparch = FIAPARCH(q=0)
@@ -1995,6 +2007,10 @@ def test_fiaparch_no_beta(setup):
19952007
# omega, phi, d, gamma, delta
19962008
assert len(bounds) == fiaparch.num_params
19972009

2010+
names = fiaparch.parameter_names()
2011+
assert "beta" not in names
2012+
assert names == ["omega", "phi", "d", "gamma", "delta"]
2013+
19982014
a, _ = fiaparch.constraints()
19992015
# No beta => FIGARCH block shrinks (5 rows), gamma (2 rows), delta (2 rows) = 9
20002016
assert a.shape[1] == 5
@@ -2022,6 +2038,7 @@ def test_fiaparch_no_asym(setup):
20222038

20232039
names = fiaparch.parameter_names()
20242040
assert "gamma" not in names
2041+
assert names == ["omega", "phi", "d", "beta", "delta"]
20252042

20262043
assert_equal(fiaparch.name, "FI Power ARCH")
20272044

@@ -2124,6 +2141,30 @@ def test_fiaparch_simulate_high_beta(setup):
21242141
fiaparch.simulate(parameters, 20, rng.simulate([]))
21252142

21262143

2144+
@pytest.mark.parametrize(
2145+
"p,o,q",
2146+
[(0, 1, 1), (1, 0, 1), (1, 1, 0), (0, 0, 1), (0, 1, 0), (1, 0, 0), (0, 0, 0)],
2147+
)
2148+
def test_fiaparch_simulate_reduced(setup, p, o, q):
2149+
fiaparch = FIAPARCH(p=p, o=o, q=q, truncation=100)
2150+
rng = Normal(seed=RandomState())
2151+
params = [1.0]
2152+
if p:
2153+
params.append(0.2)
2154+
params.append(0.4)
2155+
if q:
2156+
params.append(0.2)
2157+
if o:
2158+
params.append(-0.3)
2159+
params.append(1.5)
2160+
parameters = np.array(params)
2161+
sim_data = fiaparch.simulate(parameters, setup.t, rng.simulate([]))
2162+
assert sim_data[0].shape[0] == setup.t
2163+
assert sim_data[1].shape[0] == setup.t
2164+
assert np.all(np.isfinite(sim_data[0]))
2165+
assert np.all(np.isfinite(sim_data[1]))
2166+
2167+
21272168
def test_fiaparch_backcast_transform(setup):
21282169
fiaparch = FIAPARCH()
21292170
backcast = fiaparch.backcast(setup.resids)
@@ -2137,6 +2178,48 @@ def test_fiaparch_backcast_transform(setup):
21372178
assert result_arr.shape == (2,)
21382179
assert_allclose(result_arr, np.array([expected, expected]))
21392180

2181+
fiaparch_fd = FIAPARCH(delta=1.5)
2182+
backcast_fd = fiaparch_fd.backcast(setup.resids)
2183+
result_fd = fiaparch_fd.backcast_transform(backcast_fd)
2184+
assert np.isscalar(result_fd)
2185+
expected_fd = float(np.sqrt(backcast_fd) ** 1.5)
2186+
assert_allclose(result_fd, expected_fd)
2187+
2188+
result_fd_arr = fiaparch_fd.backcast_transform(
2189+
np.array([backcast_fd, backcast_fd])
2190+
)
2191+
assert result_fd_arr.shape == (2,)
2192+
assert_allclose(result_fd_arr, np.array([expected_fd, expected_fd]))
2193+
2194+
2195+
def test_fiaparch_minimal(setup):
2196+
fiaparch = FIAPARCH(p=0, q=0)
2197+
2198+
assert fiaparch.num_params == 4 # omega, d, gamma, delta
2199+
2200+
names = fiaparch.parameter_names()
2201+
assert names == ["omega", "d", "gamma", "delta"]
2202+
2203+
bounds = fiaparch.bounds(setup.resids)
2204+
assert len(bounds) == 4
2205+
2206+
a, b = fiaparch.constraints()
2207+
# FIGARCH block: omega > 0, d > 0, d < 1 => 3 rows
2208+
# gamma: 2 rows, delta: 2 rows => total 7
2209+
assert a.shape == (7, 4)
2210+
assert b.shape == (7,)
2211+
2212+
sv = fiaparch.starting_values(setup.resids)
2213+
assert sv.shape[0] == 4
2214+
2215+
backcast = fiaparch.backcast(setup.resids)
2216+
var_bounds = fiaparch.variance_bounds(setup.resids)
2217+
# omega, d, gamma, delta
2218+
parameters = np.array([1.0, 0.4, -0.3, 1.5])
2219+
sigma2 = np.zeros_like(setup.sigma2)
2220+
fiaparch.compute_variance(parameters, setup.resids, sigma2, backcast, var_bounds)
2221+
assert np.all(np.isfinite(sigma2))
2222+
21402223

21412224
def test_fiaparch_errors(setup):
21422225
with pytest.raises(ValueError, match=r"truncation must be a positive integer"):
@@ -2165,3 +2248,4 @@ def test_fiaparch_str(setup, p, o, q):
21652248
assert f"q: {q}" in s
21662249
assert f"p: {p}" in s
21672250
assert f"o: {o}" in s
2251+
assert "delta:" not in s

arch/univariate/mean.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2040,7 +2040,7 @@ def arch_model(
20402040
else: # mean == "zero"
20412041
am = ZeroMean(y, hold_back=hold_back, rescale=rescale)
20422042

2043-
if vol_model in ("arch", "garch", "figarch", "fiaparch", "egarch", "aparch") and not isinstance(
2043+
if vol in ("arch", "garch", "figarch", "fiaparch", "egarch", "aparch") and not isinstance(
20442044
p, int
20452045
):
20462046
raise TypeError(

0 commit comments

Comments
 (0)