From 88a118f5a5002d25d8cc2b9decd09082527bebee Mon Sep 17 00:00:00 2001 From: Gavin Chan Date: Wed, 15 Mar 2023 15:38:56 +0000 Subject: [PATCH] Rolling window forecast with rolling demean #633 --- arch/univariate/base.py | 17 +++++++++++++---- arch/univariate/mean.py | 3 +++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/arch/univariate/base.py b/arch/univariate/base.py index 1b8111dbbe..bd5cf4cb5b 100644 --- a/arch/univariate/base.py +++ b/arch/univariate/base.py @@ -467,6 +467,7 @@ def fix( params: Sequence[float] | ArrayLike1D, first_obs: int | DateLike | None = None, last_obs: int | DateLike | None = None, + demean: bool = False, ) -> ARCHModelFixedResult: """ Allows an ARCHModelFixedResult to be constructed from fixed parameters. @@ -481,6 +482,8 @@ def fix( First observation to use when fixing model last_obs : {int, str, datetime, Timestamp} Last observation to use when fixing model + demean: bool + Indicate to demean the fitting data y. Default is `False`. Returns ------- @@ -493,7 +496,7 @@ def fix( """ v = self.volatility - self._adjust_sample(first_obs, last_obs) + self._adjust_sample(first_obs, last_obs, demean) resids = np.asarray(self.resids(self.starting_values()), dtype=float) sigma2 = np.zeros_like(resids) backcast = v.backcast(resids) @@ -536,7 +539,10 @@ def fix( @abstractmethod def _adjust_sample( - self, first_obs: int | DateLike | None, last_obs: int | DateLike | None + self, + first_obs: int | DateLike | None, + last_obs: int | DateLike | None, + demean: bool, ) -> None: """ Performs sample adjustment for estimation @@ -547,6 +553,8 @@ def _adjust_sample( First observation to use when estimating model last_obs : {int, str, datetime, datetime64, Timestamp} Last observation to use when estimating model + demean: bool + Perform demean on fitting data y Notes ----- @@ -565,6 +573,7 @@ def fit( tol: float | None = None, options: dict[str, Any] | None = None, backcast: None | float | Float64Array = None, + demean: bool = False, ) -> ARCHModelResult: r""" Estimate model parameters @@ -627,14 +636,14 @@ def fit( v.closed_form and d.num_params == 0 and isinstance(v, ConstantVariance) ) - self._adjust_sample(first_obs, last_obs) + self._adjust_sample(first_obs, last_obs, demean) resids = np.asarray(self.resids(self.starting_values()), dtype=float) self._check_scale(resids) if self.scale != 1.0: # Scale changed, rescale data and reset model self._y = cast(np.ndarray, self.scale * np.asarray(self._y_original)) - self._adjust_sample(first_obs, last_obs) + self._adjust_sample(first_obs, last_obs, demean) resids = np.asarray(self.resids(self.starting_values()), dtype=float) if backcast is None: diff --git a/arch/univariate/mean.py b/arch/univariate/mean.py index e36ba944e8..082227a5e2 100644 --- a/arch/univariate/mean.py +++ b/arch/univariate/mean.py @@ -672,6 +672,7 @@ def _adjust_sample( self, first_obs: None | int | DateLike, last_obs: None | int | DateLike, + demean: bool, ) -> None: index = self._y_series.index _first_obs_index = cutoff_to_index(first_obs, index, 0) @@ -681,6 +682,8 @@ def _adjust_sample( raise ValueError("first_obs and last_obs produce in an empty array.") self._fit_indices = [_first_obs_index, _last_obs_index] self._fit_y = self._y[_first_obs_index:_last_obs_index] + if demean: + self._fit_y = np.mean(self._fit_y) reg = self.regressors self._fit_regressors = reg[_first_obs_index:_last_obs_index] self.volatility.start, self.volatility.stop = self._fit_indices