Spaces:
Sleeping
Sleeping
Download models/auto_arima_model.py from 3VVM/MYTHOSLIVE: direct link, hf CLI and curl.
- Browser
- Download file 4.37 kB
-
https://huggingface.co/spaces/3VVM/MYTHOSLIVE/resolve/main/models/auto_arima_model.py
- Command line
-
hf download hf://spaces/3VVM/MYTHOSLIVE/models/auto_arima_model.py
-
curl -L -o auto_arima_model.py https://huggingface.co/spaces/3VVM/MYTHOSLIVE/resolve/main/models/auto_arima_model.py
4.37 kB
| import threading | |
| import numpy as np | |
| import pandas as pd | |
| from models.base_model import BaseForecastModel, causal_exogenous, validate_history_and_horizon | |
| from models.dependencies import fit_arima, import_arima, import_kpss, quiet_fit | |
| from features.feature_pipeline import validate_features | |
| class AutoArimaModel(BaseForecastModel): | |
| name = 'Auto-ARIMA' | |
| def __init__(self, max_p=5, max_q=5, max_d=2, order=None, refit_order_every=None): | |
| self.max_p = max_p | |
| self.max_q = max_q | |
| self.max_d = max_d | |
| self._cached_order = order | |
| self.refit_order_every = refit_order_every | |
| self._calls_since_search = 0 | |
| self._lock = threading.Lock() | |
| self.last_diagnostics = {} | |
| def _find_d(self, series: pd.Series) -> int: | |
| kpss = import_kpss(self.name) | |
| s = series.copy() | |
| d = 0 | |
| if float(s.std()) < 1e-12: | |
| return 0 | |
| with quiet_fit(): | |
| while d < self.max_d: | |
| try: | |
| _, p_value, _, _ = kpss(s, regression='c', nlags='auto') | |
| except Exception as error: | |
| raise ValueError(f'Auto-ARIMA stationarity test failed ({type(error).__name__}): {error}') from error | |
| if p_value >= 0.05: | |
| break | |
| s = s.diff().dropna() | |
| d += 1 | |
| return d | |
| def _aic(self, series: pd.Series, order, exog: pd.DataFrame=None) -> float: | |
| arima = import_arima(self.name) | |
| try: | |
| return fit_arima(arima(series, exog=exog, order=order)).aic | |
| except Exception as error: | |
| self.last_diagnostics.setdefault('candidate_errors', []).append( | |
| {'order': order, 'error': f'{type(error).__name__}: {error}'}) | |
| return np.inf | |
| def _stepwise_search(self, series: pd.Series, d: int, exog: pd.DataFrame=None): | |
| # A compact Hyndman-style candidate set is more stable for noisy | |
| # intraday data than repeatedly walking a large grid at every origin. | |
| candidates = [(0, 0), (1, 0), (0, 1), (1, 1), (2, 0), (0, 2), (2, 1), (1, 2)] | |
| candidates = [(p, q) for p, q in candidates if p <= self.max_p and q <= self.max_q] | |
| best_order, best_aic = (None, np.inf) | |
| with quiet_fit(): | |
| for p, q in candidates: | |
| aic = self._aic(series, (p, d, q), exog=exog) | |
| if aic < best_aic: | |
| best_aic, best_order = (aic, (p, q)) | |
| if best_order is None: | |
| raise ValueError(f'No Auto-ARIMA candidate converged on this training window: {self.last_diagnostics}') | |
| p, q = best_order | |
| return (p, d, q) | |
| def predict(self, history: pd.Series, horizon: int=1, features: pd.DataFrame=None) -> list: | |
| with self._lock: | |
| self.last_diagnostics = {} | |
| validate_history_and_horizon(history, horizon, self.name) | |
| series = pd.Series(history).astype(float).reset_index(drop=True) | |
| validate_history_and_horizon(series, horizon, self.name) | |
| if len(series) < 20: | |
| raise ValueError('Auto-ARIMA needs at least 20 candles of history.') | |
| arima = import_arima(self.name) | |
| if (series <= 0).any(): | |
| raise ValueError('Auto-ARIMA needs strictly positive prices for its log-price model.') | |
| exog = future_exog = None | |
| if features is not None: | |
| validate_features(features, expected_len=len(series), model_name='Auto-ARIMA') | |
| exog, future_exog = causal_exogenous(features, horizon, self.name) | |
| if exog is not None: | |
| series = series.iloc[1:].reset_index(drop=True) | |
| series = np.log(series) | |
| need_search = self._cached_order is None | |
| if self.refit_order_every and self._calls_since_search >= self.refit_order_every: | |
| need_search = True | |
| if need_search: | |
| d = self._find_d(series) | |
| self._cached_order = self._stepwise_search(series, d, exog=exog) | |
| self._calls_since_search = 0 | |
| self._calls_since_search += 1 | |
| from models.arima_model import ArimaModel | |
| forecast = ArimaModel._fit_forecast(self, arima, series, self._cached_order, horizon, exog, future_exog) | |
| return forecast.tolist() | |