Spaces:
Sleeping
Sleeping
| import sys | |
| import os | |
| import pandas as pd | |
| import numpy as np | |
| # Bulletproof pathing: Force Python to look in both the current folder AND the parent folder | |
| # This ensures it finds models.py regardless of whether this file is run from /tests or the root. | |
| _this_dir = os.path.dirname(os.path.abspath(__file__)) | |
| sys.path.insert(0, _this_dir) | |
| sys.path.insert(0, os.path.dirname(_this_dir)) | |
| # Import the specific function from the engine | |
| from models import model_bsts | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| # 1. FREQUENCY GATING TESTS | |
| # βββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_bsts_frequency_gate_aborts_on_daily(): | |
| """ | |
| BSTS overfits on daily stock returns (which are essentially white noise). | |
| This test verifies that the engine completely aborts if 'periods' > 52 | |
| (e.g., daily data with 252 periods). | |
| """ | |
| rng = np.random.default_rng(42) | |
| # Synthetic daily data | |
| returns_df = pd.DataFrame({'AAPL': rng.normal(0.0005, 0.01, size=100)}) | |
| # Act: Request a BSTS fit expecting 252 trading periods in a year | |
| res = model_bsts(returns_df, periods=252, silent=True) | |
| # Assert: The model should instantly reject the request to prevent noise-fitting | |
| assert res.expected_returns is None or res.expected_returns.empty | |
| def test_bsts_short_series_fallback(): | |
| """ | |
| BSTS requires a minimum amount of data to establish stationary momentum. | |
| If the series is too short (e.g., < 12 months), it must output | |
| massive uncertainty so the Black-Litterman bridge safely ignores it. | |
| """ | |
| rng = np.random.default_rng(42) | |
| # Only 10 months of data | |
| returns_df = pd.DataFrame({'TSLA': rng.normal(0.01, 0.05, size=10)}) | |
| res = model_bsts(returns_df, periods=12, silent=True) | |
| # Assert: Should output 0 expected return and massive (1000.0) uncertainty | |
| assert np.isclose(res.expected_returns['TSLA'], 0.0) | |
| assert np.isclose(res.uncertainties['TSLA'], 1000.0) | |
| def test_bsts_valid_signal(): | |
| """ | |
| If a monthly time series exhibits momentum, the BSTS model should | |
| correctly output a genuine variance (uncertainty < 1000.0). | |
| """ | |
| rng = np.random.default_rng(42) | |
| n = 60 | |
| # Create a synthetic AR(1) process with strong positive momentum | |
| series = np.zeros(n) | |
| ar_coef = 0.8 | |
| for i in range(1, n): | |
| series[i] = ar_coef * series[i-1] + rng.normal(0, 0.005) | |
| returns_df = pd.DataFrame({'TRENDY_ASSET': series}) | |
| # Act | |
| res = model_bsts(returns_df, periods=12, silent=True) | |
| # Assert: The model must recognize the signal. The uncertainty should be the | |
| # legitimate standard error of the forecast, NOT the 1000.0 rejection flag. | |
| assert res.uncertainties['TRENDY_ASSET'] < 1000.0 | |
| assert not pd.isna(res.expected_returns['TRENDY_ASSET']) |