def get_HARCH(process, iters, params): # HARCH if params == None: params = {'LAGS': [1, 5, 17], 'INIT': [1], 'PARAMS': [0.2, 0.05, 0.01]} metadata = {"NAME": process, "number_of_iterations": iters} metadata.update(params) model = HARCH(lags=params['LAGS']) param_vect = params['INIT'] + params['PARAMS'] y = model.simulate(nobs=iters, burn=1000, parameters=param_vect, rng=np.random.standard_normal) true_var = y[1] * sps.norm.ppf(0.01) y = y[0] data = pd.DataFrame(data={'Returns': y, 'True_VAR_0.01': true_var}) return metadata, data
SP500 = 100 * sp500.load()["Adj Close"].pct_change().dropna() MEAN_MODELS = [ HARX(SP500, lags=[1, 5]), ARX(SP500, lags=2), ConstantMean(SP500), ZeroMean(SP500), ] VOLATILITIES = [ ConstantVariance(), GARCH(), FIGARCH(), EWMAVariance(lam=0.94), MIDASHyperbolic(), HARCH(lags=[1, 5, 22]), RiskMetrics2006(), APARCH(), EGARCH(), ] MODEL_SPECS = list(product(MEAN_MODELS, VOLATILITIES)) IDS = [ f"{str(mean).split('(')[0]}-{str(vol).split('(')[0]}" for mean, vol in MODEL_SPECS ] @pytest.fixture(params=MODEL_SPECS, ids=IDS) def model_spec(request): mean, vol = request.param
def return_sampler_garch( N_train: int, mean_process: str = "Constant", lags_mean_process: int = None, vol_process: str = "GARCH", distr_noise: str = "normal", seed: int = None, seed_param: int = None, p_arg: list = None, ) -> Tuple[np.ndarray, pd.Series]: # https://stats.stackexchange.com/questions/61824/how-to-interpret-garch-parameters # https://arch.readthedocs.io/en/latest/univariate/introduction.html # https://arch.readthedocs.io/en/latest/univariate/volatility.html # https://github.com/bashtage/arch/blob/master/arch/univariate/volatility.py """ Generates financial returns driven by mean-reverting factors. Parameters ---------- N_train: int Length of the experiment mean_process: str Mean process for the returns. It can be 'Constant' or 'AR' lags_mean_process: int Order of autoregressive lag if mean_process is AR vol_process: str Volatility process for the returns. It can be 'GARCH', 'EGARCH', 'TGARCH', 'ARCH', 'HARCH', 'FIGARCH' or 'Constant'. Note that different volatility processes requires different parameter, which are hard coded. If you want to pass them explicitly, use p_arg. distr_noise: str Distribution for the unpredictable component of the returns. It can be 'normal', 'studt', 'skewstud' or 'ged'. Note that different distributions requires different parameter, which are hard coded. If you want to pass them explicitly, use p_arg. seed: int Seed for experiment reproducibility seed_param: int Seed for drawing randomly the parameters needed for the simulation. The ranges provided are obtained as average lower and upper bounds of several GARCH-type model fitting on real financial time-series. p_arg: pd.Series Pandas series of parameters that you want to pass explicitly. They need to be passed in the right order. Check documentation of the arch python package (https://arch.readthedocs.io/en/latest/index.html) for more details. Returns ------- simulations['data'].values: np.ndarray Simulated series of returns p: pd.Series Series of parameters used for simulation """ names = [] vals = [] if seed_param is None: seed_param = seed rng = np.random.RandomState(seed_param) # choose mean process if mean_process == "Constant": model = ConstantMean(None) names.append("const") if seed_param: vals.append(rng.uniform(0.01, 0.09)) else: vals.append(0.0) elif mean_process == "AR": model = ARX(None, lags=lags_mean_process) names.append("const") vals.append(0.0) if seed_param: for i in range(lags_mean_process): names.append("lag{}".format(i)) vals.append(rng.uniform(-0.09, 0.09)) else: for i in range(lags_mean_process): names.append("lag{}".format(i)) vals.append(0.9) else: return print("This mean process doesn't exist or it's not available.") sys.exit() # choose volatility process if vol_process == "GARCH": model.volatility = GARCH(p=1, q=1) names.extend(["omega", "alpha", "beta"]) if seed_param: om = rng.uniform(0.03, 0.1) alph = rng.uniform(0.05, 0.1) b = rng.uniform(0.86, 0.92) garch_p = np.array([om, alph, b]) / (np.array([om, alph, b]).sum()) else: om = 0.01 alph = 0.05 b = 0.94 garch_p = np.array([om, alph, b]) vals.extend(list(garch_p)) elif vol_process == "ARCH": model.volatility = GARCH(p=1, q=0) names.extend(["omega", "alpha"]) if seed_param: om = rng.uniform(1.4, 4.0) alph = rng.uniform(0.1, 0.6) else: om = 0.01 alph = 0.4 garch_p = np.array([om, alph]) vals.extend(list(garch_p)) elif vol_process == "HARCH": model.volatility = HARCH(lags=[1, 5, 22]) names.extend(["omega", "alpha[1]", "alpha[5]", "alpha[22]"]) if seed_param: om = rng.uniform(1.2, 0.5) alph1 = rng.uniform(0.01, 0.1) alph5 = rng.uniform(0.05, 0.3) alph22 = rng.uniform(0.4, 0.7) else: om = 0.01 alph1 = 0.05 alph5 = 0.15 alph22 = 0.5 garch_p = np.array([om, alph1, alph5, alph22]) vals.extend(list(garch_p)) elif vol_process == "FIGARCH": model.volatility = FIGARCH(p=1, q=1) names.extend(["omega", "phi", "d", "beta"]) if seed_param: om = rng.uniform(0.05, 0.03) phi = rng.uniform(0.1, 0.35) d = rng.uniform(0.3, 0.5) beta = rng.uniform(0.4, 0.7) else: om = 0.01 phi = 0.2 d = 0.2 beta = 0.55 garch_p = np.array([om, phi, d, beta]) vals.extend(list(garch_p)) elif vol_process == "TGARCH": model.volatility = GARCH(p=1, o=1, q=1) names.extend(["omega", "alpha", "gamma", "beta"]) if seed_param: om = rng.uniform(0.02, 0.15) alph = rng.uniform(0.01, 0.07) gamma = rng.uniform(0.03, 0.1) b = rng.uniform(0.88, 0.94) else: om = 0.01 alph = 0.05 gamma = 0.04 b = 0.90 garch_p = np.array([om, alph, gamma, b]) vals.extend(list(garch_p)) elif vol_process == "EGARCH": model.volatility = EGARCH(p=1, o=1, q=1) names.extend(["omega", "alpha", "gamma", "beta"]) if seed_param: om = rng.uniform(0.01, 0.03) alph = rng.uniform(0.06, 0.17) gamma = rng.uniform(-0.05, -0.02) b = rng.uniform(0.97, 0.99) garch_p = np.array([om, alph, gamma, b]) / (np.array( [om, alph, gamma, b]).sum()) else: om = 0.01 alph = 0.05 gamma = -0.02 b = 0.94 garch_p = np.array([om, alph, gamma, b]) vals.extend(list(garch_p)) elif vol_process == "Constant": model.volatility = ConstantVariance() names.append("sigma_const") vals.append(rng.uniform(0.02, 0.05)) else: print("This volatility process doesn't exist or it's not available.") sys.exit() if distr_noise == "normal": model.distribution = Normal(np.random.RandomState(seed)) elif distr_noise == "studt": model.distribution = StudentsT(np.random.RandomState(seed)) names.append("nu") if seed_param: vals.append(rng.randint(6.0, 10.0)) else: vals.append(8.0) elif distr_noise == "skewstud": model.distribution = SkewStudent(np.random.RandomState(seed)) names.extend(["nu", "lambda"]) if seed_param: vals.extend([rng.uniform(6.0, 10.0), rng.uniform(-0.1, 0.1)]) else: vals.extend([8.0, 0.05]) elif distr_noise == "ged": model.distribution = GeneralizedError(np.random.RandomState(seed)) names.append("nu") if seed_param: vals.append(rng.uniform(1.05, 3.0)) else: vals.append(2.0) else: print("This noise distribution doesn't exist or it's not available.") sys.exit() p = pd.Series(data=vals, index=names) if p_arg: p = p_arg simulations = model.simulate(p, N_train) / 100 return simulations["data"].values, p
def model(self): self.__model.volatility = HARCH(lags=self.__lags) return self.__model
def __bic_with(self, lag_list): self.__model.volatility = HARCH(lags=lag_list) result = self.__model.fit(update_freq=0, disp='off') return round(result.bic, 4)