Spaces:
Running
Running
Download models/arima_model.py from 3VVM/MYTHOSLIVE: direct link, hf CLI and curl.
- Browser
- Download file 4.79 kB
-
https://huggingface.co/spaces/3VVM/MYTHOSLIVE/resolve/main/models/arima_model.py
- Command line
-
hf download hf://spaces/3VVM/MYTHOSLIVE/models/arima_model.py
-
curl -L -o arima_model.py https://huggingface.co/spaces/3VVM/MYTHOSLIVE/resolve/main/models/arima_model.py
4.79 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, quiet_fit | |
| from features.feature_pipeline import validate_features | |
| class ArimaModel(BaseForecastModel): | |
| name = 'ARIMA' | |
| def __init__(self, max_p=3, max_d=2, max_q=3, order=None, refit_order_every=None): | |
| self.max_p = max_p | |
| self.max_d = max_d | |
| self.max_q = max_q | |
| self._cached_order = order | |
| self.refit_order_every = refit_order_every | |
| self._calls_since_search = 0 | |
| self._lock = threading.Lock() | |
| self.last_diagnostics = {} | |
| def _best_order(self, series: pd.Series, exog: pd.DataFrame=None): | |
| arima = import_arima(self.name) | |
| best_aic = np.inf | |
| best_order = None | |
| # AIC values from different integration orders have different likelihood | |
| # bases. Select d on the training sample, then compare only p/q at that d. | |
| from models.auto_arima_model import AutoArimaModel | |
| selected_d = AutoArimaModel(max_d=self.max_d)._find_d(series) | |
| with quiet_fit(): | |
| candidates = [(p, d, q) | |
| for d in [selected_d] | |
| for p in range(self.max_p + 1) | |
| for q in range(self.max_q + 1) | |
| if p + q <= 2] | |
| for order in candidates: | |
| try: | |
| fit = fit_arima(arima(series, exog=exog, order=order)) | |
| except Exception: | |
| continue | |
| if fit.aic < best_aic: | |
| best_aic = fit.aic | |
| best_order = order | |
| if best_order is None: | |
| raise ValueError('No ARIMA candidate converged on this training window.') | |
| return best_order | |
| def _fit_forecast(self, arima, series, order, horizon, exog, future_exog): | |
| """Fit a positive-price ARIMA with deterministic, real fallbacks.""" | |
| candidates = [order, (0, 1, 0), (1, 1, 0), (0, 1, 1), (1, 1, 1)] | |
| seen = set() | |
| last_error = None | |
| for candidate in candidates: | |
| if candidate in seen: | |
| continue | |
| seen.add(candidate) | |
| try: | |
| with quiet_fit(): | |
| fit = fit_arima(arima(series, exog=exog, order=candidate)) | |
| forecast = fit.forecast(steps=horizon, exog=future_exog) if exog is not None else fit.forecast(steps=horizon) | |
| values = np.exp(np.asarray(forecast, dtype=float)) | |
| if values.shape == (horizon,) and np.isfinite(values).all() and (values > 0).all(): | |
| self._cached_order = candidate | |
| self.last_diagnostics = {'converged': True, 'selected_order': order, 'executed_order': candidate, 'order_fallback': candidate != order, 'feature_policy': 'lagged one candle; unknown future covariates persist'} | |
| return values | |
| except Exception as error: | |
| last_error = error | |
| raise ValueError(f'ARIMA could not fit a stable positive-price model: {last_error}') from None | |
| def predict(self, history: pd.Series, horizon: int=1, features: pd.DataFrame=None) -> list: | |
| with self._lock: | |
| validate_history_and_horizon(history, horizon, self.name) | |
| self.last_diagnostics = {} | |
| series = pd.Series(history).astype(float).reset_index(drop=True) | |
| validate_history_and_horizon(series, horizon, self.name) | |
| if len(series) < 20: | |
| raise ValueError('ARIMA needs at least 20 candles of history.') | |
| arima = import_arima(self.name) | |
| exog = future_exog = None | |
| if features is not None: | |
| validate_features(features, expected_len=len(series), model_name='ARIMA') | |
| exog, future_exog = causal_exogenous(features, horizon, self.name) | |
| if exog is not None: | |
| series = series.iloc[1:].reset_index(drop=True) | |
| if (series <= 0).any(): | |
| raise ValueError('ARIMA needs strictly positive prices for its log-price model.') | |
| log_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: | |
| self._cached_order = self._best_order(log_series, exog=exog) | |
| self._calls_since_search = 0 | |
| self._calls_since_search += 1 | |
| forecast = self._fit_forecast(arima, log_series, self._cached_order, horizon, exog, future_exog) | |
| return [float(x) for x in forecast] | |