def test_skewstudent(self): dist = SkewStudent() eta, lam = 4.0, .5 ll1 = dist.loglikelihoood(np.array([eta, lam]), self.resids, self.sigma2) # Direct calculation of PDF, then log const_c = gamma((eta+1)/2) / ((np.pi*(eta-2))**.5 * gamma(eta/2)) const_a = 4*lam*const_c*(eta-2)/(eta-1) const_b = (1 + 3*lam**2 - const_a**2)**.5 resids = self.resids / self.sigma2**.5 pdf = const_b * const_c / self.sigma2**.5 \ * (1 + 1/(eta-2) *((const_b * resids + const_a) /(1 + np.sign(resids + const_a / const_b) * lam))**2)**(-(eta+1)/2) ll2 = np.log(pdf).sum() assert_almost_equal(ll1, ll2) assert_equal(dist.num_params, 2) bounds = dist.bounds(self.resids) assert_equal(len(bounds), 2) A, b = dist.constraints() assert_equal(A.shape, (4, 2)) k = stats.kurtosis(self.resids, fisher=False) sv = max((4.0 * k - 6.0) / (k - 3.0) if k > 3.75 else 12.0, 4.0) assert_array_equal(dist.starting_values(self.resids), np.array([sv, 0.])) assert_raises(ValueError, dist.simulate, np.array([1.5, 0.])) assert_raises(ValueError, dist.simulate, np.array([4., 1.5])) assert_raises(ValueError, dist.simulate, np.array([4., -1.5])) assert_raises(ValueError, dist.simulate, np.array([1.5, 1.5]))
def test_warnings(self): garch = GARCH() parameters = np.array([0.1, 0.2, 0.8, 4.0]) studt = StudentsT() with warnings.catch_warnings(record=True) as w: garch.simulate(parameters, 1000, studt.simulate([4.0])) assert_equal(len(w), 1) garch = GARCH() parameters = np.array([0.1, 0.2, 0.8, 4.0, 0.5]) skewstud = SkewStudent() with warnings.catch_warnings(record=True) as w: garch.simulate(parameters, 1000, skewstud.simulate([4.0, 0.5])) assert_equal(len(w), 1) harch = HARCH(lags=[1, 5, 22]) parameters = np.array([0.1, 0.2, 0.4, 0.5]) with warnings.catch_warnings(record=True) as w: harch.simulate(parameters, 1000, studt.simulate([4.0])) assert_equal(len(w), 1)
def test_warnings_nonstationary_garch(self): garch = GARCH() parameters = np.array([0.1, 0.2, 0.8, 4.0, 0.5]) skewstud = SkewStudent() with pytest.warns(InitialValueWarning): garch.simulate(parameters, 1000, skewstud.simulate([4.0, 0.5]))
def arch_model(y, x=None, mean='Constant', lags=0, vol='Garch', p=1, o=0, q=1, power=2.0, dist='Normal', hold_back=None): """ Convenience function to simplify initialization of ARCH models Parameters ---------- y : {ndarray, Series, None} The dependent variable x : {np.array, DataFrame}, optional Exogenous regressors. Ignored if model does not permit exogenous regressors. mean : str, optional Name of the mean model. Currently supported options are: 'Constant', 'Zero', 'ARX' and 'HARX' lags : int or list (int), optional Either a scalar integer value indicating lag length or a list of integers specifying lag locations. vol : str, optional Name of the volatility model. Currently supported options are: 'GARCH' (default), "EGARCH', 'ARCH' and 'HARCH' p : int, optional Lag order of the symmetric innovation o : int, optional Lag order of the asymmetric innovation q : int, optional Lag order of lagged volatility or equivalent power : float, optional Power to use with GARCH and related models dist : int, optional Name of the error distribution. Currently supported options are: * Normal: 'normal', 'gaussian' (default) * Students's t: 't', 'studentst' * Skewed Student's t: 'skewstudent', 'skewt' * Generalized Error Distribution: 'ged', 'generalized error" hold_back : int Number of observations at the start of the sample to exclude when estimating model parameters. Used when comparing models with different lag lengths to estimate on the common sample. Returns ------- model : ARCHModel Configured ARCH model Examples -------- >>> import datetime as dt >>> import pandas_datareader.data as web >>> djia = web.get_data_fred('DJIA') >>> returns = 100 * djia['DJIA'].pct_change().dropna() A basic GARCH(1,1) with a constant mean can be constructed using only the return data >>> from arch.univariate import arch_model >>> am = arch_model(returns) Alternative mean and volatility processes can be directly specified >>> am = arch_model(returns, mean='AR', lags=2, vol='harch', p=[1, 5, 22]) This example demonstrates the construction of a zero mean process with a TARCH volatility process and Student t error distribution >>> am = arch_model(returns, mean='zero', p=1, o=1, q=1, ... power=1.0, dist='StudentsT') Notes ----- Input that are not relevant for a particular specification, such as `lags` when `mean='zero'`, are silently ignored. """ known_mean = ('zero', 'constant', 'harx', 'har', 'ar', 'arx', 'ls') known_vol = ('arch', 'garch', 'harch', 'constant', 'egarch') known_dist = ('normal', 'gaussian', 'studentst', 't', 'skewstudent', 'skewt', 'ged', 'generalized error') mean = mean.lower() vol = vol.lower() dist = dist.lower() if mean not in known_mean: raise ValueError('Unknown model type in mean') if vol.lower() not in known_vol: raise ValueError('Unknown model type in vol') if dist.lower() not in known_dist: raise ValueError('Unknown model type in dist') if mean == 'zero': am = ZeroMean(y, hold_back=hold_back) elif mean == 'constant': am = ConstantMean(y, hold_back=hold_back) elif mean == 'harx': am = HARX(y, x, lags, hold_back=hold_back) elif mean == 'har': am = HARX(y, None, lags, hold_back=hold_back) elif mean == 'arx': am = ARX(y, x, lags, hold_back=hold_back) elif mean == 'ar': am = ARX(y, None, lags, hold_back=hold_back) else: am = LS(y, x, hold_back=hold_back) if vol == 'constant': v = ConstantVariance() elif vol == 'arch': v = ARCH(p=p) elif vol == 'garch': v = GARCH(p=p, o=o, q=q, power=power) elif vol == 'egarch': v = EGARCH(p=p, o=o, q=q) else: # vol == 'harch' v = HARCH(lags=p) if dist in ('skewstudent', 'skewt'): d = SkewStudent() elif dist in ('studentst', 't'): d = StudentsT() elif dist in ('ged', 'generalized error'): d = GeneralizedError() else: # ('gaussian', 'normal') d = Normal() am.volatility = v am.distribution = d return am
def test_skewstudent(self): dist = SkewStudent() eta, lam = 4.0, .5 ll1 = dist.loglikelihood(np.array([eta, lam]), self.resids, self.sigma2) # Direct calculation of PDF, then log const_c = gamma( (eta + 1) / 2) / ((np.pi * (eta - 2))**.5 * gamma(eta / 2)) const_a = 4 * lam * const_c * (eta - 2) / (eta - 1) const_b = (1 + 3 * lam**2 - const_a**2)**.5 resids = self.resids / self.sigma2**.5 pow = (-(eta + 1) / 2) pdf = const_b * const_c / self.sigma2 ** .5 * \ (1 + 1 / (eta - 2) * ((const_b * resids + const_a) / (1 + np.sign(resids + const_a / const_b) * lam)) ** 2) ** pow ll2 = np.log(pdf).sum() assert_almost_equal(ll1, ll2) assert_equal(dist.num_params, 2) bounds = dist.bounds(self.resids) assert_equal(len(bounds), 2) a, b = dist.constraints() assert_equal(a.shape, (4, 2)) k = stats.kurtosis(self.resids, fisher=False) sv = max((4.0 * k - 6.0) / (k - 3.0) if k > 3.75 else 12.0, 4.0) assert_array_equal(dist.starting_values(self.resids), np.array([sv, 0.])) with pytest.raises(ValueError): dist.simulate(np.array([1.5, 0.])) with pytest.raises(ValueError): dist.simulate(np.array([4., 1.5])) with pytest.raises(ValueError): dist.simulate(np.array([4., -1.5])) with pytest.raises(ValueError): dist.simulate(np.array([1.5, 1.5]))
def arch_model( y: Optional[ArrayLike], x: Optional[ArrayLike] = None, mean: str = "Constant", lags: Optional[Union[int, List[int], NDArray]] = 0, vol: str = "Garch", p: Union[int, List[int]] = 1, o: int = 0, q: int = 1, power: float = 2.0, dist: str = "Normal", hold_back: Optional[int] = None, rescale: Optional[bool] = None, ) -> HARX: """ Initialization of common ARCH model specifications Parameters ---------- y : {ndarray, Series, None} The dependent variable x : {np.array, DataFrame}, optional Exogenous regressors. Ignored if model does not permit exogenous regressors. mean : str, optional Name of the mean model. Currently supported options are: 'Constant', 'Zero', 'LS', 'AR', 'ARX', 'HAR' and 'HARX' lags : int or list (int), optional Either a scalar integer value indicating lag length or a list of integers specifying lag locations. vol : str, optional Name of the volatility model. Currently supported options are: 'GARCH' (default), 'ARCH', 'EGARCH', 'FIARCH' and 'HARCH' p : int, optional Lag order of the symmetric innovation o : int, optional Lag order of the asymmetric innovation q : int, optional Lag order of lagged volatility or equivalent power : float, optional Power to use with GARCH and related models dist : int, optional Name of the error distribution. Currently supported options are: * Normal: 'normal', 'gaussian' (default) * Students's t: 't', 'studentst' * Skewed Student's t: 'skewstudent', 'skewt' * Generalized Error Distribution: 'ged', 'generalized error" hold_back : int Number of observations at the start of the sample to exclude when estimating model parameters. Used when comparing models with different lag lengths to estimate on the common sample. rescale : bool Flag indicating whether to automatically rescale data if the scale of the data is likely to produce convergence issues when estimating model parameters. If False, the model is estimated on the data without transformation. If True, than y is rescaled and the new scale is reported in the estimation results. Returns ------- model : ARCHModel Configured ARCH model Examples -------- >>> import datetime as dt >>> import pandas_datareader.data as web >>> djia = web.get_data_fred('DJIA') >>> returns = 100 * djia['DJIA'].pct_change().dropna() A basic GARCH(1,1) with a constant mean can be constructed using only the return data >>> from arch.univariate import arch_model >>> am = arch_model(returns) Alternative mean and volatility processes can be directly specified >>> am = arch_model(returns, mean='AR', lags=2, vol='harch', p=[1, 5, 22]) This example demonstrates the construction of a zero mean process with a TARCH volatility process and Student t error distribution >>> am = arch_model(returns, mean='zero', p=1, o=1, q=1, ... power=1.0, dist='StudentsT') Notes ----- Input that are not relevant for a particular specification, such as `lags` when `mean='zero'`, are silently ignored. """ known_mean = ("zero", "constant", "harx", "har", "ar", "arx", "ls") known_vol = ("arch", "figarch", "garch", "harch", "constant", "egarch") known_dist = ( "normal", "gaussian", "studentst", "t", "skewstudent", "skewt", "ged", "generalized error", ) mean = mean.lower() vol = vol.lower() dist = dist.lower() if mean not in known_mean: raise ValueError("Unknown model type in mean") if vol.lower() not in known_vol: raise ValueError("Unknown model type in vol") if dist.lower() not in known_dist: raise ValueError("Unknown model type in dist") if mean == "harx": am = HARX(y, x, lags, hold_back=hold_back, rescale=rescale) elif mean == "har": am = HARX(y, None, lags, hold_back=hold_back, rescale=rescale) elif mean == "arx": am = ARX(y, x, lags, hold_back=hold_back, rescale=rescale) elif mean == "ar": am = ARX(y, None, lags, hold_back=hold_back, rescale=rescale) elif mean == "ls": am = LS(y, x, hold_back=hold_back, rescale=rescale) elif mean == "constant": am = ConstantMean(y, hold_back=hold_back, rescale=rescale) else: # mean == "zero" am = ZeroMean(y, hold_back=hold_back, rescale=rescale) if vol in ("arch", "garch", "figarch", "egarch") and not isinstance(p, int): raise TypeError( "p must be a scalar int for all volatility processes except HARCH." ) if vol == "constant": v: VolatilityProcess = ConstantVariance() elif vol == "arch": assert isinstance(p, int) v = ARCH(p=p) elif vol == "figarch": assert isinstance(p, int) v = FIGARCH(p=p, q=q) elif vol == "garch": assert isinstance(p, int) v = GARCH(p=p, o=o, q=q, power=power) elif vol == "egarch": assert isinstance(p, int) v = EGARCH(p=p, o=o, q=q) else: # vol == 'harch' v = HARCH(lags=p) if dist in ("skewstudent", "skewt"): d: Distribution = SkewStudent() elif dist in ("studentst", "t"): d = StudentsT() elif dist in ("ged", "generalized error"): d = GeneralizedError() else: # ('gaussian', 'normal') d = Normal() am.volatility = v am.distribution = d return am
def __init__(self, random_state=None): DistributionMixin.__init__(self) SS.__init__(self, random_state)
from numpy import exp, inf, log, nan, ones_like, pi from numpy.testing import assert_almost_equal, assert_equal import pytest from scipy.integrate import quad from scipy.special import gammaln from arch.univariate.distribution import ( GeneralizedError, Normal, SkewStudent, StudentsT, ) DISTRIBUTIONS = [ (SkewStudent(), [6, -0.1]), (SkewStudent(), [6, -0.5]), (SkewStudent(), [6, 0.1]), (SkewStudent(), [6, 0.5]), (GeneralizedError(), [1.5]), (GeneralizedError(), [2.1]), (StudentsT(), [6]), (StudentsT(), [7]), (Normal(), None), ] @pytest.mark.parametrize("dist, params", DISTRIBUTIONS) def test_moment(dist, params): """ Ensures that Distribtion.moment and .partial_moment agree with numeric integrals for order n=0,...,5 and z=+/-1,...,+/-5
import pytest from arch.univariate.distribution import (SkewStudent, Normal, StudentsT, GeneralizedError) from numpy import exp, inf, log, nan, pi from numpy.testing import assert_equal, assert_almost_equal from scipy.integrate import quad from scipy.special import gammaln DISTRIBUTIONS = [(SkewStudent(), [6, -0.1]), (SkewStudent(), [6, -0.5]), (SkewStudent(), [6, 0.1]), (SkewStudent(), [6, 0.5]), (GeneralizedError(), [1.5]), (GeneralizedError(), [2.1]), (StudentsT(), [6]), (StudentsT(), [7]), (Normal(), None)] @pytest.mark.parametrize('dist, params', DISTRIBUTIONS) def test_moment(dist, params): """ Ensures that Distribtion.moment and .partial_moment agree with numeric integrals for order n=0,...,5 and z=+/-1,...,+/-5 Parameters ---------- dist : distribution.Distribution The distribution whose moments are being checked params : List List of parameters """ assert_equal(dist.moment(-1, params), nan) assert_equal(dist.partial_moment(-1, 0., params), nan)