Round 2: reverting power law and cycle-on-powerlaw tests; one-time holdout run.
Add TrendReversionVol: deviations from the power-law trend follow a daily AR(1), so uncertainty levels off, optionally plus trend-parameter uncertainty with an autocorrelation-adjusted effective sample size. Two experiments, run under the unchanged verdict rule: - powerlaw-ou: +21% to +45% vs powerlaw at 2-4 years, but slightly negative point estimates at 1 month make it inconclusive. - cycle-on-powerlaw: inconclusive (+18% at 2 years, negative elsewhere). The holdout (outcomes after 2024-11-26) was scored once, for the four candidates fixed beforehand. powerlaw is the best long-horizon forecast (+45% and +58% vs the random walk at 2 and 3 years); nothing beats the random walk inside a year; cycle fails badly. Results are in the README.
This commit is contained in:
@@ -6,7 +6,7 @@ from btcmodel.experiments import EXPERIMENTS
|
||||
from btcmodel.halving import GENESIS
|
||||
from btcmodel.models.drift import CycleDrift, PowerLawDrift, ShrunkDrift
|
||||
from btcmodel.models.shape import StudentT
|
||||
from btcmodel.models.volatility import ReversionVol, TrailingVol
|
||||
from btcmodel.models.volatility import ReversionVol, TrailingVol, TrendReversionVol
|
||||
|
||||
from .test_models import synthetic_prices
|
||||
|
||||
@@ -64,3 +64,22 @@ def test_student_t_shape_has_unit_variance_and_fatter_tails():
|
||||
q = StudentT(4).standard_quantiles(None, np.array([1]))[0]
|
||||
assert q[-1] > 2.576 # beyond the normal 99.5% quantile
|
||||
assert np.interp(0.8413, np.linspace(0.005, 0.995, 100), q) < 1.0 # thinner shoulders
|
||||
|
||||
|
||||
def test_trend_reversion_vol_levels_off():
|
||||
# A power-law trend plus AR(1) deviations with a ~70-day half-life.
|
||||
rng = np.random.default_rng(0)
|
||||
dates = pd.date_range("2011-01-01", "2024-11-26", freq="D", name="date")
|
||||
t = (dates - GENESIS).days.to_numpy()
|
||||
gap = np.zeros(len(t))
|
||||
for i in range(1, len(t)):
|
||||
gap[i] = 0.99 * gap[i - 1] + 0.03 * rng.standard_normal()
|
||||
prices = pd.DataFrame({"close": np.exp(-40 + 5.8 * np.log(t) + gap)}, index=dates)
|
||||
|
||||
horizons = np.array([30, 365, 1460, 5000])
|
||||
plain = TrendReversionVol().sd(prices, horizons)
|
||||
with_params = TrendReversionVol(parameter_uncertainty=True).sd(prices, horizons)
|
||||
assert np.all(np.diff(plain) >= 0)
|
||||
stationary = 0.03 / np.sqrt(1 - 0.99**2)
|
||||
assert plain[-1] == pytest.approx(stationary, rel=0.15)
|
||||
assert np.all(with_params >= plain)
|
||||
|
||||
Reference in New Issue
Block a user