Spaces:
Running
Running
Download tests/test_models_with_features.py from 3VVM/MYTHOSLIVE: direct link, hf CLI and curl.
- Browser
- Download file 7.82 kB
-
https://huggingface.co/spaces/3VVM/MYTHOSLIVE/resolve/main/tests/test_models_with_features.py
- Command line
-
hf download hf://spaces/3VVM/MYTHOSLIVE/tests/test_models_with_features.py
-
curl -L -o test_models_with_features.py https://huggingface.co/spaces/3VVM/MYTHOSLIVE/resolve/main/tests/test_models_with_features.py
7.82 kB
| import importlib.util | |
| import unittest | |
| import numpy as np | |
| import pandas as pd | |
| from features.feature_pipeline import compute_feature_frame | |
| def _have(module_name: str) -> bool: | |
| return importlib.util.find_spec(module_name) is not None | |
| _HAVE_STATSMODELS = _have('statsmodels') | |
| _HAVE_ARCH = _have('arch') | |
| _HAVE_MOIRAI = _have('uni2ts') and _have('gluonts') | |
| _HAVE_TIMESFM = _have('timesfm') and _have('torch') | |
| def _synthetic_ohlcv(n: int, seed: int=42) -> pd.DataFrame: | |
| rng = np.random.default_rng(seed) | |
| steps = rng.normal(loc=0.0, scale=0.4, size=n) | |
| close = 100 + np.cumsum(steps) | |
| close = np.abs(close) + 5.0 | |
| open_ = close + rng.normal(0, 0.1, n) | |
| high = np.maximum(open_, close) + np.abs(rng.normal(0, 0.2, n)) | |
| low = np.minimum(open_, close) - np.abs(rng.normal(0, 0.2, n)) | |
| volume = rng.integers(100, 10000, n).astype(float) | |
| idx = pd.date_range('2024-01-01', periods=n, freq='min') | |
| return pd.DataFrame({'Open': open_, 'High': high, 'Low': low, 'Close': close, 'Volume': volume}, index=idx) | |
| def _warm_close_and_features(df: pd.DataFrame, feats: pd.DataFrame): | |
| complete = ~feats.isna().any(axis=1).to_numpy() | |
| start = int(np.argmax(complete)) if complete.any() else len(df) | |
| return (df['Close'].reset_index(drop=True).iloc[start:], feats.iloc[start:].reset_index(drop=True)) | |
| class TestArimaFamilyAlignment(unittest.TestCase): | |
| def test_returns_alignment_has_no_off_by_one(self): | |
| n = 10 | |
| prices = pd.Series(100 + np.arange(n, dtype=float)) | |
| features = pd.Series([float(i) for i in range(n)]) | |
| log_returns = np.log(prices / prices.shift(1)).dropna().reset_index(drop=True) | |
| exog = features.iloc[1:].reset_index(drop=True) | |
| self.assertEqual(len(exog), len(log_returns)) | |
| for k in range(len(log_returns)): | |
| self.assertEqual(exog[k], float(k + 1)) | |
| class TestArimaWithFeatures(unittest.TestCase): | |
| def setUp(self): | |
| self.df = _synthetic_ohlcv(120) | |
| self.feats = compute_feature_frame(self.df) | |
| self.close, self.warm_feats = _warm_close_and_features(self.df, self.feats) | |
| def test_baseline_unchanged_shape(self): | |
| from models.arima_model import ArimaModel | |
| m = ArimaModel(max_p=2, max_d=1, max_q=2) | |
| out = m.predict(self.close, horizon=3) | |
| self.assertEqual(len(out), 3) | |
| self.assertTrue(all(np.isfinite(out))) | |
| def test_with_features_runs_and_is_finite(self): | |
| from models.arima_model import ArimaModel | |
| m = ArimaModel(max_p=2, max_d=1, max_q=2) | |
| out = m.predict(self.close, horizon=3, features=self.warm_feats) | |
| self.assertEqual(len(out), 3) | |
| self.assertTrue(all(np.isfinite(out))) | |
| def test_mismatched_features_length_raises(self): | |
| from models.arima_model import ArimaModel | |
| m = ArimaModel(max_p=2, max_d=1, max_q=2) | |
| short_window = self.warm_feats.iloc[:-5].reset_index(drop=True) | |
| with self.assertRaises(ValueError): | |
| m.predict(self.close, horizon=3, features=short_window) | |
| class TestAutoArimaWithFeatures(unittest.TestCase): | |
| def setUp(self): | |
| self.df = _synthetic_ohlcv(120) | |
| self.feats = compute_feature_frame(self.df) | |
| self.close, self.warm_feats = _warm_close_and_features(self.df, self.feats) | |
| def test_with_features_runs_and_is_finite(self): | |
| from models.auto_arima_model import AutoArimaModel | |
| m = AutoArimaModel(max_p=3, max_q=3, max_d=1) | |
| out = m.predict(self.close, horizon=2, features=self.warm_feats) | |
| self.assertEqual(len(out), 2) | |
| self.assertTrue(all(np.isfinite(out))) | |
| class TestArimaGarchWithFeatures(unittest.TestCase): | |
| def setUp(self): | |
| self.df = _synthetic_ohlcv(150) | |
| self.feats = compute_feature_frame(self.df) | |
| self.close, self.warm_feats = _warm_close_and_features(self.df, self.feats) | |
| def test_with_features_runs_and_is_finite(self): | |
| from models.arima_garch_model import ArimaGarchModel | |
| m = ArimaGarchModel(max_p=2, max_q=2) | |
| out = m.predict(self.close, horizon=2, features=self.warm_feats) | |
| self.assertEqual(len(out), 2) | |
| self.assertTrue(all(np.isfinite(out))) | |
| self.assertIsNotNone(m.last_volatility_forecast) | |
| class TestMoiraiWithFeatures(unittest.TestCase): | |
| def test_with_features_runs_and_is_finite(self): | |
| from models.moirai_model import MoiraiModel | |
| df = _synthetic_ohlcv(60) | |
| feats = compute_feature_frame(df) | |
| close, warm_feats = _warm_close_and_features(df, feats) | |
| m = MoiraiModel(context_length=60, num_samples=10) | |
| out = m.predict(close, horizon=2, features=warm_feats) | |
| self.assertEqual(len(out), 2) | |
| self.assertTrue(all(np.isfinite(out))) | |
| class TestTimesFMWithFeatures(unittest.TestCase): | |
| def test_with_features_runs_and_is_finite(self): | |
| from models.timesfm_model import TimesFMModel | |
| df = _synthetic_ohlcv(64) | |
| feats = compute_feature_frame(df) | |
| close, warm_feats = _warm_close_and_features(df, feats) | |
| m = TimesFMModel(max_context=64) | |
| out = m.predict(close, horizon=2, features=warm_feats) | |
| self.assertEqual(len(out), 2) | |
| self.assertTrue(all(np.isfinite(out))) | |
| def test_short_input_below_compiled_context_is_finite(self): | |
| from models.timesfm_model import TimesFMModel | |
| df = _synthetic_ohlcv(60, seed=99) | |
| close = 40000 + df['Close'].reset_index(drop=True) | |
| m = TimesFMModel(max_context=512) | |
| out = m.predict(close, horizon=2) | |
| self.assertEqual(len(out), 2) | |
| self.assertTrue(all(np.isfinite(out)), f'TimesFM NaN on short window: {out}') | |
| def test_repeated_window_lengths_stay_finite(self): | |
| from models.timesfm_model import TimesFMModel | |
| df = _synthetic_ohlcv(120, seed=7) | |
| close = 40000 + df['Close'].reset_index(drop=True) | |
| m = TimesFMModel(max_context=512) | |
| for w in (40, 100, 40): | |
| out = m.predict(close.iloc[-w:], horizon=1) | |
| self.assertEqual(len(out), 1) | |
| self.assertTrue(np.isfinite(out[0]), f'TimesFM NaN at w={w}: {out[0]}') | |
| class TestInputContractGuards(unittest.TestCase): | |
| def test_nan_history_raises_for_all_models(self): | |
| from models.registry import fresh_model | |
| rng = np.random.default_rng(3) | |
| good = pd.Series(100 + np.cumsum(rng.normal(0, 0.5, 200))) | |
| for name in ('ARIMA', 'Auto-ARIMA', 'ARIMA-GARCH', 'Moirai'): | |
| m = fresh_model(name) | |
| h = good.copy() | |
| h.iloc[50] = np.nan | |
| with self.assertRaises(ValueError, msg=name): | |
| m.predict(h, horizon=1) | |
| m = fresh_model('TimesFM') | |
| h = good.copy() | |
| h.iloc[50] = np.nan | |
| with self.assertRaises(ValueError): | |
| m.predict(h, horizon=1) | |
| def test_horizon_below_one_raises_for_all_models(self): | |
| from models.registry import fresh_model | |
| rng = np.random.default_rng(3) | |
| good = pd.Series(100 + np.cumsum(rng.normal(0, 0.5, 200))) | |
| for name in ('ARIMA', 'Auto-ARIMA', 'ARIMA-GARCH', 'Moirai', 'TimesFM'): | |
| m = fresh_model(name) | |
| for bad in (0, -1): | |
| with self.assertRaises(ValueError, msg=f'{name} horizon={bad}'): | |
| m.predict(good, horizon=bad) | |
| if __name__ == '__main__': | |
| unittest.main() | |